From a517a4c1b6afe3c3c202801249dcbea41cfdec4c Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Tue, 30 Apr 2019 10:52:55 -0500 Subject: SONAR-11969 Make Edition available for analyzers in the Scanner API --- .../src/main/java/org/sonar/api/SonarEdition.java | 38 +++++++++++ .../src/main/java/org/sonar/api/SonarRuntime.java | 32 ++++++--- .../batch/sensor/internal/SensorContextTester.java | 4 +- .../java/org/sonar/api/internal/ApiVersion.java | 51 -------------- .../org/sonar/api/internal/MetadataLoader.java | 77 +++++++++++++++++++++ .../org/sonar/api/internal/SonarRuntimeImpl.java | 20 ++++-- .../src/test/java/org/sonar/api/PluginTest.java | 2 +- .../org/sonar/api/internal/ApiVersionTest.java | 56 --------------- .../org/sonar/api/internal/MetadataLoaderTest.java | 79 ++++++++++++++++++++++ .../sonar/api/internal/SonarRuntimeImplTest.java | 10 ++- 10 files changed, 238 insertions(+), 131 deletions(-) create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/SonarEdition.java delete mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/internal/ApiVersion.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/internal/MetadataLoader.java delete mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/internal/ApiVersionTest.java create mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/internal/MetadataLoaderTest.java (limited to 'sonar-plugin-api/src') diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/SonarEdition.java b/sonar-plugin-api/src/main/java/org/sonar/api/SonarEdition.java new file mode 100644 index 00000000000..7cfa5b45ca7 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/SonarEdition.java @@ -0,0 +1,38 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.api; + +public enum SonarEdition { + COMMUNITY("Community"), + DEVELOPER("Developer"), + ENTERPRISE("Enterprise"), + DATACENTER("Data Center"), + SONARCLOUD("SonarCloud"); + + private final String label; + + SonarEdition(String label) { + this.label = label; + } + + public String getLabel() { + return label; + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/SonarRuntime.java b/sonar-plugin-api/src/main/java/org/sonar/api/SonarRuntime.java index 0eecfb6977e..2579288a396 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/SonarRuntime.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/SonarRuntime.java @@ -20,11 +20,10 @@ package org.sonar.api; import javax.annotation.concurrent.Immutable; - -import org.sonar.api.scanner.ScannerSide; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.ce.ComputeEngineSide; +import org.sonar.api.scanner.ScannerSide; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.Version; import org.sonarsource.api.sonarlint.SonarLintSide; @@ -53,7 +52,7 @@ import org.sonarsource.api.sonarlint.SonarLintSide; * // @since 6.1 * public void bar(); * } - * + * * // Plugin extension * public class MyExtension { * private final SonarRuntime sonarRuntime; @@ -77,7 +76,7 @@ import org.sonarsource.api.sonarlint.SonarLintSide; * * *

- * Note that {@link Sensor} extensions can directly get {@link SonarRuntime} through + * Note that {@link Sensor} extensions can directly get {@link SonarRuntime} through * {@link SensorContext#runtime()}, without using constructor injection: *

*
@@ -127,16 +126,17 @@ import org.sonarsource.api.sonarlint.SonarLintSide;
  * 
* *

- * As this component was introduced in version 6.0, the pattern described above can't be - * exactly applied when plugin must support version 5.6 Long Term Support. In this case plugin - * should use {@link SonarQubeVersion}, for example through {@link Plugin.Context#getSonarQubeVersion()} or - * {@link SensorContext#getSonarQubeVersion()}. + * As this component was introduced in version 6.0, the pattern described above can't be + * exactly applied when plugin must support version 5.6 Long Term Support. In this case plugin + * should use {@link SonarQubeVersion}, for example through {@link Plugin.Context#getSonarQubeVersion()} or + * {@link SensorContext#getSonarQubeVersion()}. *

* *

* Unit tests of plugin extensions can create instances of {@link SonarRuntime} * via {@link org.sonar.api.internal.SonarRuntimeImpl}. *

+ * * @since 6.0 */ @ScannerSide @@ -153,19 +153,29 @@ public interface SonarRuntime { *
* Since 6.3, the returned version includes the build number in the fourth field, for * example {@code "6.3.0.12345"}. - */ + */ Version getApiVersion(); /** * The product being executed at runtime. It targets analysers so that they can implement - * different behaviours in SonarQube and SonarLint. + * different behaviours in SonarQube/SonarCloud and SonarLint. */ SonarProduct getProduct(); /** - * The SonarQube stack being executed at runtime. + * The SonarQube/SonarCloud stack being executed at runtime. + * * @throws UnsupportedOperationException if {@link #getProduct()} is not equal to {@link SonarProduct#SONARQUBE} */ SonarQubeSide getSonarQubeSide(); + /** + * The SonarQube/SonarCloud edition being executed at runtime. + * Note that there is a specific edition for SonarCloud. + * + * @throws UnsupportedOperationException if {@link #getProduct()} is not equal to {@link SonarProduct#SONARQUBE} + * @since 7.8 + */ + SonarEdition getEdition(); + } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java index 0ee4c211f7b..edf595685d4 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java @@ -80,7 +80,7 @@ import org.sonar.api.config.Configuration; import org.sonar.api.config.Settings; import org.sonar.api.config.internal.ConfigurationBridge; import org.sonar.api.config.internal.MapSettings; -import org.sonar.api.internal.ApiVersion; +import org.sonar.api.internal.MetadataLoader; import org.sonar.api.internal.SonarRuntimeImpl; import org.sonar.api.measures.Metric; import org.sonar.api.scanner.fs.InputProject; @@ -121,7 +121,7 @@ public class SensorContextTester implements SensorContext { this.sensorStorage = new InMemorySensorStorage(); this.project = new DefaultInputProject(ProjectDefinition.create().setKey("projectKey").setBaseDir(moduleBaseDir.toFile()).setWorkDir(moduleBaseDir.resolve(".sonar").toFile())); this.module = new DefaultInputModule(ProjectDefinition.create().setKey("projectKey").setBaseDir(moduleBaseDir.toFile()).setWorkDir(moduleBaseDir.resolve(".sonar").toFile())); - this.runtime = SonarRuntimeImpl.forSonarQube(ApiVersion.load(System2.INSTANCE), SonarQubeSide.SCANNER); + this.runtime = SonarRuntimeImpl.forSonarQube(MetadataLoader.loadVersion(System2.INSTANCE), SonarQubeSide.SCANNER, MetadataLoader.loadEdition(System2.INSTANCE)); } public static SensorContextTester create(File moduleBaseDir) { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/internal/ApiVersion.java b/sonar-plugin-api/src/main/java/org/sonar/api/internal/ApiVersion.java deleted file mode 100644 index 5b68a5d7aa4..00000000000 --- a/sonar-plugin-api/src/main/java/org/sonar/api/internal/ApiVersion.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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.api.internal; - -import com.google.common.io.Resources; -import java.io.IOException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import org.sonar.api.utils.System2; -import org.sonar.api.utils.Version; - -/** - * For internal use - * - * @since 6.0 - */ -public class ApiVersion { - - private static final String FILE_PATH = "/sonar-api-version.txt"; - - private ApiVersion() { - // only static methods - } - - public static Version load(System2 system) { - try { - URL url = system.getResource(FILE_PATH); - String versionInFile = Resources.toString(url, StandardCharsets.UTF_8); - return Version.parse(versionInFile); - } catch (IOException e) { - throw new IllegalStateException("Can not load " + FILE_PATH + " from classpath", e); - } - } -} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/internal/MetadataLoader.java b/sonar-plugin-api/src/main/java/org/sonar/api/internal/MetadataLoader.java new file mode 100644 index 00000000000..d22a4ec2aa6 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/internal/MetadataLoader.java @@ -0,0 +1,77 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.api.internal; + +import com.google.common.io.Resources; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import org.sonar.api.SonarEdition; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.Version; + +import static org.apache.commons.lang.StringUtils.trimToEmpty; + +/** + * For internal use + * + * @since 7.8 + */ +public class MetadataLoader { + + private static final String VERSION_FILE_PATH = "/sonar-api-version.txt"; + private static final String EDITION_FILE_PATH = "/sonar-edition.txt"; + + private MetadataLoader() { + // only static methods + } + + public static Version loadVersion(System2 system) { + try { + URL url = system.getResource(VERSION_FILE_PATH); + String versionInFile = Resources.toString(url, StandardCharsets.UTF_8); + return Version.parse(versionInFile); + } catch (IOException e) { + throw new IllegalStateException("Can not load " + VERSION_FILE_PATH + " from classpath", e); + } + } + + public static SonarEdition loadEdition(System2 system) { + try { + URL url = system.getResource(EDITION_FILE_PATH); + if (url == null) { + return SonarEdition.COMMUNITY; + } + String editionInFile = Resources.toString(url, StandardCharsets.UTF_8); + return parseEdition(editionInFile); + } catch (IOException e) { + throw new IllegalStateException("Can not load " + EDITION_FILE_PATH + " from classpath", e); + } + } + + static SonarEdition parseEdition(String edition) { + String str = trimToEmpty(edition.toUpperCase()); + try { + return SonarEdition.valueOf(str); + } catch (IllegalArgumentException e) { + throw new IllegalStateException(String.format("Invalid edition found in '%s': '%s'", EDITION_FILE_PATH, str)); + } + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/internal/SonarRuntimeImpl.java b/sonar-plugin-api/src/main/java/org/sonar/api/internal/SonarRuntimeImpl.java index 6601412ac28..6395546469f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/internal/SonarRuntimeImpl.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/internal/SonarRuntimeImpl.java @@ -21,6 +21,7 @@ package org.sonar.api.internal; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; +import org.sonar.api.SonarEdition; import org.sonar.api.SonarProduct; import org.sonar.api.SonarQubeSide; import org.sonar.api.SonarRuntime; @@ -38,10 +39,13 @@ public class SonarRuntimeImpl implements SonarRuntime { private final Version version; private final SonarProduct product; private final SonarQubeSide sonarQubeSide; + private final SonarEdition edition; - private SonarRuntimeImpl(Version version, SonarProduct product, @Nullable SonarQubeSide sonarQubeSide) { + private SonarRuntimeImpl(Version version, SonarProduct product, @Nullable SonarQubeSide sonarQubeSide, @Nullable SonarEdition edition) { + this.edition = edition; requireNonNull(product); checkArgument((product == SonarProduct.SONARQUBE) == (sonarQubeSide != null), "sonarQubeSide should be provided only for SonarQube product"); + checkArgument((product == SonarProduct.SONARQUBE) == (edition != null), "edition should be provided only for SonarQube product"); this.version = requireNonNull(version); this.product = product; this.sonarQubeSide = sonarQubeSide; @@ -65,18 +69,26 @@ public class SonarRuntimeImpl implements SonarRuntime { return sonarQubeSide; } + @Override + public SonarEdition getEdition() { + if (sonarQubeSide == null) { + throw new UnsupportedOperationException("Can only be called in SonarQube"); + } + return edition; + } + /** * Create an instance for SonarQube runtime environment. */ - public static SonarRuntime forSonarQube(Version version, SonarQubeSide side) { - return new SonarRuntimeImpl(version, SonarProduct.SONARQUBE, side); + public static SonarRuntime forSonarQube(Version version, SonarQubeSide side, SonarEdition edition) { + return new SonarRuntimeImpl(version, SonarProduct.SONARQUBE, side, edition); } /** * Create an instance for SonarLint runtime environment. */ public static SonarRuntime forSonarLint(Version version) { - return new SonarRuntimeImpl(version, SonarProduct.SONARLINT, null); + return new SonarRuntimeImpl(version, SonarProduct.SONARLINT, null, null); } } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/PluginTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/PluginTest.java index 70064a5b00c..53b84c86164 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/PluginTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/PluginTest.java @@ -34,7 +34,7 @@ public class PluginTest { @Test public void test_context() { - SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(VERSION_5_6, SonarQubeSide.SERVER); + SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(VERSION_5_6, SonarQubeSide.SERVER, SonarEdition.COMMUNITY); MapSettings settings = new MapSettings().setProperty("foo", "bar"); Plugin.Context context = new PluginContextImpl.Builder() .setSonarRuntime(runtime) diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/internal/ApiVersionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/internal/ApiVersionTest.java deleted file mode 100644 index 4013878e9dd..00000000000 --- a/sonar-plugin-api/src/test/java/org/sonar/api/internal/ApiVersionTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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.api.internal; - -import java.io.File; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.sonar.api.utils.System2; -import org.sonar.api.utils.Version; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -public class ApiVersionTest { - - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - @Test - public void load_version_from_file_in_classpath() { - Version version = ApiVersion.load(System2.INSTANCE); - assertThat(version).isNotNull(); - assertThat(version.major()).isGreaterThanOrEqualTo(5); - } - - @Test - public void throw_ISE_if_fail_to_load_version() throws Exception { - expectedException.expect(IllegalStateException.class); - expectedException.expectMessage("Can not load /sonar-api-version.txt from classpath"); - - System2 system = spy(System2.class); - when(system.getResource(anyString())).thenReturn(new File("target/unknown").toURI().toURL()); - ApiVersion.load(system); - } - -} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/internal/MetadataLoaderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/internal/MetadataLoaderTest.java new file mode 100644 index 00000000000..d62ebdcf762 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/internal/MetadataLoaderTest.java @@ -0,0 +1,79 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.api.internal; + +import java.io.File; +import java.net.MalformedURLException; +import org.sonar.api.SonarEdition; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.Version; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MetadataLoaderTest { + private System2 system = mock(System2.class); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void load_version_from_file_in_classpath() { + Version version = MetadataLoader.loadVersion(System2.INSTANCE); + assertThat(version).isNotNull(); + assertThat(version.major()).isGreaterThanOrEqualTo(5); + } + + @Test + public void load_edition_from_file_in_classpath() { + SonarEdition edition = MetadataLoader.loadEdition(System2.INSTANCE); + assertThat(edition).isNotNull(); + } + + @Test + public void load_edition_defaults_to_community_if_file_not_found() throws MalformedURLException { + when(system.getResource(anyString())).thenReturn(new File("target/unknown").toURI().toURL()); + SonarEdition edition = MetadataLoader.loadEdition(System2.INSTANCE); + assertThat(edition).isEqualTo(SonarEdition.COMMUNITY); + } + + @Test + public void throw_ISE_if_edition_is_invalid() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Invalid edition found in '/sonar-edition.txt': 'TRASH'"); + + MetadataLoader.parseEdition("trash"); + } + + @Test + public void throw_ISE_if_fail_to_load_version() throws Exception { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Can not load /sonar-api-version.txt from classpath"); + + when(system.getResource(anyString())).thenReturn(new File("target/unknown").toURI().toURL()); + MetadataLoader.loadVersion(system); + } + +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/internal/SonarRuntimeImplTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/internal/SonarRuntimeImplTest.java index 5179ef530d9..8e439af9339 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/internal/SonarRuntimeImplTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/internal/SonarRuntimeImplTest.java @@ -19,6 +19,7 @@ */ package org.sonar.api.internal; +import org.sonar.api.SonarEdition; import org.assertj.core.api.Assertions; import org.junit.Rule; import org.junit.Test; @@ -30,7 +31,6 @@ import org.sonar.api.utils.Version; import static org.assertj.core.api.Assertions.assertThat; - public class SonarRuntimeImplTest { private static final Version A_VERSION = Version.parse("6.0"); @@ -40,7 +40,7 @@ public class SonarRuntimeImplTest { @Test public void sonarQube_environment() { - SonarRuntime apiVersion = SonarRuntimeImpl.forSonarQube(A_VERSION, SonarQubeSide.SCANNER); + SonarRuntime apiVersion = SonarRuntimeImpl.forSonarQube(A_VERSION, SonarQubeSide.SCANNER, SonarEdition.COMMUNITY); assertThat(apiVersion.getApiVersion()).isEqualTo(A_VERSION); assertThat(apiVersion.getProduct()).isEqualTo(SonarProduct.SONARQUBE); assertThat(apiVersion.getSonarQubeSide()).isEqualTo(SonarQubeSide.SCANNER); @@ -60,10 +60,8 @@ public class SonarRuntimeImplTest { } @Test(expected = IllegalArgumentException.class) - public void sonarqube_requires_side() throws Exception { - SonarRuntimeImpl.forSonarQube(A_VERSION, null); + public void sonarqube_requires_side() { + SonarRuntimeImpl.forSonarQube(A_VERSION, null, null); } - - } -- cgit v1.2.3