import org.sonar.api.utils.System2;
import org.sonar.api.utils.internal.TempFolderCleaner;
import org.sonar.core.config.CorePropertyDefinitions;
+import org.sonar.core.platform.SonarQubeVersionProvider;
import org.sonar.core.util.UuidFactoryImpl;
import org.sonar.db.DaoModule;
import org.sonar.db.DatabaseChecker;
add(platform, properties);
addExtraRootComponents();
add(
+ new SonarQubeVersionProvider(),
ServerSettings.class,
ServerImpl.class,
UuidFactoryImpl.INSTANCE,
import org.sonar.core.component.DefaultResourceTypes;
import org.sonar.core.config.CorePropertyDefinitions;
import org.sonar.core.issue.tracking.Tracker;
+import org.sonar.core.platform.SonarQubeVersionProvider;
public class BatchComponents {
private BatchComponents() {
public static Collection<Object> all(AnalysisMode analysisMode) {
List<Object> components = Lists.newArrayList(
+ new SonarQubeVersionProvider(),
DefaultResourceTypes.get(),
// Tasks
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.platform;
+
+import com.google.common.io.Resources;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import org.picocontainer.injectors.ProviderAdapter;
+import org.sonar.api.SonarQubeVersion;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.Version;
+
+public class SonarQubeVersionProvider extends ProviderAdapter {
+
+ private static final String FILE_PATH = "/sq-version.txt";
+
+ private SonarQubeVersion version = null;
+
+ public SonarQubeVersion provide(System2 system) {
+ if (version == null) {
+ try {
+ URL url = system.getResource(FILE_PATH);
+ String versionInFile = Resources.toString(url, StandardCharsets.UTF_8);
+ version = new SonarQubeVersion(Version.parse(versionInFile));
+ } catch (IOException e) {
+ throw new IllegalStateException("Can not load " + FILE_PATH + " from classpath", e);
+ }
+ }
+ return version;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.platform;
+
+import java.io.File;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.SonarQubeVersion;
+import org.sonar.api.utils.System2;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SonarQubeVersionProviderTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ SonarQubeVersionProvider underTest = new SonarQubeVersionProvider();
+
+ @Test
+ public void create() {
+ SonarQubeVersion version = underTest.provide(System2.INSTANCE);
+ assertThat(version).isNotNull();
+ assertThat(version.get().major()).isGreaterThanOrEqualTo(5);
+ }
+
+ @Test
+ public void cache_version() {
+ SonarQubeVersion version1 = underTest.provide(System2.INSTANCE);
+ SonarQubeVersion version2 = underTest.provide(System2.INSTANCE);
+ assertThat(version1).isSameAs(version2);
+ }
+
+ @Test
+ public void throw_ISE_if_fail_to_load_version() throws Exception {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Can not load /sq-version.txt from classpath");
+
+ System2 system = mock(System2.class);
+ when(system.getResource(anyString())).thenReturn(new File("target/unknown").toURL());
+ underTest.provide(system);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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;
+
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.batch.BatchSide;
+import org.sonar.api.server.ServerSide;
+import org.sonar.api.utils.Version;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Version of SonarQube at runtime. This component can be injected as a dependency
+ * of plugin extensions. The main usage for a plugin is to benefit from new APIs
+ * while keeping backward-compatibility with previous versions of SonarQube.
+
+ * Example: a plugin needs to use an API introduced in version 5.6 ({@code AnApi} in the following
+ * snippet) and still requires to support version 5.5 at runtime.
+ * <p></p>
+ * <pre>
+ * // Component provided by sonar-plugin-api
+ * // @since 5.5
+ * public interface AnApi {
+ * // implicitly since 5.5
+ * public void foo();
+ *
+ * // @since 5.6
+ * public void bar();
+ * }
+ *
+ * // Component provided by plugin
+ * public class MyExtension {
+ * private final SonarQubeVersion sonarQubeVersion;
+ * private final AnApi api;
+ *
+ * public MyExtension(SonarQubeVersion sonarQubeVersion, AnApi api) {
+ * this.sonarQubeVersion = sonarQubeVersion;
+ * this.api = api;
+ * }
+ *
+ * public void doSomething() {
+ * // assume that runtime is 5.5+
+ * api.foo();
+ *
+ * if (sonarQubeVersion.isGreaterThanOrEqual(SonarQubeVersion.V5_6)) {
+ * api.bar();
+ * }
+ * }
+ * }
+ * </pre>
+ * <p></p>
+ * The minimal supported version of SonarQube is verified at runtime. As plugin is built
+ * with sonar-plugin-api 5.6, we assume that the plugin requires v5.6 or greater at runtime.
+ * For this reason the plugin must default which is the minimal supported version
+ * in the configuration of sonar-packaging-maven-plugin 1.16+:
+ * <p></p>
+ * <pre>
+ * <packaging>sonar-plugin</packaging>
+ *
+ * <dependencies>
+ * <dependency>
+ * <groupId>org.sonarsource.sonarqube</groupId>
+ * <artifactId>sonar-plugin-api</artifactId>
+ * <version>5.6</version>
+ * <scope>provided</scope>
+ * </dependency>
+ * </dependencies>
+ *
+ * <build>
+ * <plugins>
+ * <plugin>
+ * <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
+ * <artifactId>sonar-packaging-maven-plugin</artifactId>
+ * <version>1.16</version>
+ * <extensions>true</extensions>
+ * <configuration>
+ * <!-- Override the default value 5.6 which is guessed from sonar-plugin-api dependency -->
+ * <sonarQubeMinVersion>5.5</sonarQubeMinVersion>
+ * </configuration>
+ * </plugin>
+ * </plugins>
+ * </build>
+ * </pre>
+ *
+ *
+ * @since 5.5
+ */
+@BatchSide
+@ServerSide
+@Immutable
+public class SonarQubeVersion {
+
+ /**
+ * Constant for version 5.5
+ */
+ public static final Version V5_5 = Version.create(5, 5);
+
+ /**
+ * Constant for version 5.6
+ */
+ public static final Version V5_6 = Version.create(5, 6);
+
+ private final Version version;
+
+ public SonarQubeVersion(Version version) {
+ requireNonNull(version);
+ this.version = version;
+ }
+
+ public Version get() {
+ return this.version;
+ }
+
+ public boolean isGreaterThanOrEqual(Version than) {
+ return this.version.compareTo(than) >= 0;
+ }
+}
import javax.annotation.concurrent.Immutable;
import static java.lang.Integer.parseInt;
+import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang.StringUtils.substringAfter;
import static org.apache.commons.lang.StringUtils.substringBefore;
import static org.apache.commons.lang.StringUtils.trimToEmpty;
/**
- * TODO document equals/comparison without qualifier
+ * Version composed of 3 integer-sequences (major, minor and patch fields) and optionally a qualifier.
+ * <p></p>
+ * Examples: 1.0, 1.0.0, 1.2.3, 1.2-beta1, 1.2.1-beta-1
+ * <p></p>
+ * <h3>IMPORTANT NOTE</h3>
+ * Qualifier is ignored when comparing objects (methods {@link #equals(Object)}, {@link #hashCode()}
+ * and {@link #compareTo(Version)}).
+ * <p></p>
+ * <pre>
+ * assertThat(Version.parse("1.2")).isEqualTo(Version.parse("1.2-beta1"));
+ * assertThat(Version.parse("1.2").compareTo(Version.parse("1.2-beta1"))).isZero();
+ * </pre>
+ *
* @since 5.5
*/
@Immutable
private final String qualifier;
private Version(int major, int minor, int patch, String qualifier) {
+ requireNonNull(qualifier, "Version qualifier must not be null");
this.major = major;
this.minor = minor;
this.patch = patch;
}
/**
- * @return non-null suffix
+ * @return non-null suffix. Empty if absent, else the suffix without the first character "-"
*/
public String qualifier() {
return qualifier;
}
+ /**
+ * Convert a {@link String} to a Version. Supported formats:
+ * <ul>
+ * <li>1</li>
+ * <li>1.2</li>
+ * <li>1.2.3</li>
+ * <li>1-beta-1</li>
+ * <li>1.2-beta-1</li>
+ * <li>1.2.3-beta-1</li>
+ * </ul>
+ * Note that the optional qualifier is the part after the first "-".
+ *
+ * @throws IllegalArgumentException if parameter is badly formatted, for example
+ * if it defines 4 integer-sequences.
+ */
public static Version parse(String text) {
String s = trimToEmpty(text);
String qualifier = substringAfter(s, QUALIFIER_SEPARATOR);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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;
+
+import org.junit.Test;
+import org.sonar.api.utils.Version;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SonarQubeVersionTest {
+
+ @Test
+ public void isGte() {
+ Version version = Version.parse("1.2.3");
+ SonarQubeVersion qubeVersion = new SonarQubeVersion(version);
+ assertThat(qubeVersion.get()).isEqualTo(version);
+ assertThat(qubeVersion.isGreaterThanOrEqual(version)).isTrue();
+ assertThat(qubeVersion.isGreaterThanOrEqual(Version.parse("1.1"))).isTrue();
+ assertThat(qubeVersion.isGreaterThanOrEqual(Version.parse("1.3"))).isFalse();
+ }
+}
assertThat(one).isEqualTo(parse("1.0"));
assertThat(one).isEqualTo(parse("1.0.0"));
assertThat(one).isNotEqualTo(parse("1.2.3"));
+ assertThat(one).isNotEqualTo("1");
assertThat(parse("1.2.3")).isEqualTo(parse("1.2.3"));
assertThat(parse("1.2.3")).isNotEqualTo(parse("1.2.4"));
assertThat(parse("1.2.3-b1").toString()).isEqualTo("1.2.3-b1");
}
+ @Test
+ public void test_create() {
+ assertVersion(Version.create(1, 2), 1, 2, 0, "");
+ assertVersion(Version.create(1, 2, 3), 1, 2, 3, "");
+ assertVersion(Version.create(1, 2, 0, ""), 1, 2, 0, "");
+ assertVersion(Version.create(1, 2, 3, "build1"), 1, 2, 3, "build1");
+ assertThat(Version.create(1, 2, 3, "build1").toString()).isEqualTo("1.2.3-build1");
+
+ }
+
private static void assertVersion(Version version,
- int expectedMajor, int expectedMinor, int expectedPatch, String expectedQualifier) {
+ int expectedMajor, int expectedMinor, int expectedPatch, String expectedQualifier) {
assertThat(version.major()).isEqualTo(expectedMajor);
assertThat(version.minor()).isEqualTo(expectedMinor);
assertThat(version.patch()).isEqualTo(expectedPatch);