diff options
author | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2018-06-20 17:15:44 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2018-07-05 20:21:54 +0200 |
commit | 45f6d410d36e999607e306dcf4374d739d58677b (patch) | |
tree | 2412b212c0ebf0a0ec8765f874aa315857c37a49 /sonar-core | |
parent | 69afc80d1a5f641c4b27c84aa4a050ad10a13222 (diff) | |
download | sonarqube-45f6d410d36e999607e306dcf4374d739d58677b.tar.gz sonarqube-45f6d410d36e999607e306dcf4374d739d58677b.zip |
LICENSE-96 implement support for staging and new server id format
Diffstat (limited to 'sonar-core')
-rw-r--r-- | sonar-core/src/main/java/org/sonar/core/platform/ServerId.java | 164 | ||||
-rw-r--r-- | sonar-core/src/test/java/org/sonar/core/platform/ServerIdTest.java | 309 |
2 files changed, 473 insertions, 0 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/ServerId.java b/sonar-core/src/main/java/org/sonar/core/platform/ServerId.java new file mode 100644 index 00000000000..b30ee8130e8 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/platform/ServerId.java @@ -0,0 +1,164 @@ +/* + * 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.core.platform; + +import com.google.common.collect.ImmutableSet; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import org.sonar.api.CoreProperties; +import org.sonar.core.util.UuidFactory; + +import static com.google.common.base.Preconditions.checkArgument; +import static org.sonar.core.platform.ServerId.Format.DEPRECATED; +import static org.sonar.core.platform.ServerId.Format.NO_DATABASE_ID; +import static org.sonar.core.platform.ServerId.Format.WITH_DATABASE_ID; + +@Immutable +public final class ServerId { + + public static final char SPLIT_CHARACTER = '-'; + public static final int DATABASE_ID_LENGTH = 8; + public static final int DEPRECATED_SERVER_ID_LENGTH = 14; + public static final int NOT_UUID_DATASET_ID_LENGTH = 15; + public static final int UUID_DATASET_ID_LENGTH = 20; + private static final Set<Integer> ALLOWED_LENGTHS = ImmutableSet.of( + DEPRECATED_SERVER_ID_LENGTH, + NOT_UUID_DATASET_ID_LENGTH, + NOT_UUID_DATASET_ID_LENGTH + 1 + DATABASE_ID_LENGTH, + UUID_DATASET_ID_LENGTH, + UUID_DATASET_ID_LENGTH + 1 + DATABASE_ID_LENGTH); + + public enum Format { + /* server id format before 6.1 (see SONAR-6992) */ + DEPRECATED, + /* server id format before 6.7.5 and 7.3 (see LICENSE-96) */ + NO_DATABASE_ID, + WITH_DATABASE_ID + } + + private final String databaseId; + private final String datasetId; + private final Format format; + + private ServerId(@Nullable String databaseId, String datasetId) { + this.databaseId = databaseId; + this.datasetId = datasetId; + this.format = computeFormat(databaseId, datasetId); + } + + public Optional<String> getDatabaseId() { + return Optional.ofNullable(databaseId); + } + + public String getDatasetId() { + return datasetId; + } + + public Format getFormat() { + return format; + } + + private static Format computeFormat(@Nullable String databaseId, String datasetId) { + if (databaseId != null) { + return WITH_DATABASE_ID; + } + if (isDate(datasetId)) { + return DEPRECATED; + } + return NO_DATABASE_ID; + } + + public static ServerId parse(String serverId) { + String trimmed = serverId.trim(); + + int length = trimmed.length(); + checkArgument(length > 0, "serverId can't be empty"); + checkArgument(ALLOWED_LENGTHS.contains(length), "serverId does not have a supported length"); + if (length == DEPRECATED_SERVER_ID_LENGTH || length == UUID_DATASET_ID_LENGTH || length == NOT_UUID_DATASET_ID_LENGTH) { + return new ServerId(null, trimmed); + } + + int splitCharIndex = trimmed.indexOf(SPLIT_CHARACTER); + if (splitCharIndex == -1) { + return new ServerId(null, trimmed); + } + checkArgument(splitCharIndex == DATABASE_ID_LENGTH, "Unrecognized serverId format. Parts have wrong length"); + return of(trimmed.substring(0, splitCharIndex), trimmed.substring(splitCharIndex + 1)); + } + + public static ServerId of(@Nullable String databaseId, String datasetId) { + if (databaseId != null) { + int databaseIdLength = databaseId.length(); + checkArgument(databaseIdLength == DATABASE_ID_LENGTH, "Illegal databaseId length (%s)", databaseIdLength); + } + int datasetIdLength = datasetId.length(); + checkArgument(datasetIdLength == DEPRECATED_SERVER_ID_LENGTH + || datasetIdLength == NOT_UUID_DATASET_ID_LENGTH + || datasetIdLength == UUID_DATASET_ID_LENGTH, "Illegal datasetId length (%s)", datasetIdLength); + return new ServerId(databaseId, datasetId); + } + + public static ServerId create(UuidFactory uuidFactory) { + return new ServerId(null, uuidFactory.create()); + } + + /** + * Checks whether the specified value is a date according to the old format of the {@link CoreProperties#SERVER_ID}. + */ + private static boolean isDate(String value) { + try { + new SimpleDateFormat("yyyyMMddHHmmss").parse(value); + return true; + } catch (ParseException e) { + return false; + } + } + + @Override + public String toString() { + if (databaseId == null) { + return datasetId; + } + return databaseId + SPLIT_CHARACTER + datasetId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ServerId serverId = (ServerId) o; + return Objects.equals(databaseId, serverId.databaseId) && + Objects.equals(datasetId, serverId.datasetId); + } + + @Override + public int hashCode() { + return Objects.hash(databaseId, datasetId); + } +} diff --git a/sonar-core/src/test/java/org/sonar/core/platform/ServerIdTest.java b/sonar-core/src/test/java/org/sonar/core/platform/ServerIdTest.java new file mode 100644 index 00000000000..f6f15d22bab --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/platform/ServerIdTest.java @@ -0,0 +1,309 @@ +/* + * 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.core.platform; + +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Random; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.sonar.core.util.UuidFactoryImpl; + +import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; +import static org.apache.commons.lang.StringUtils.repeat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.core.platform.ServerId.DATABASE_ID_LENGTH; +import static org.sonar.core.platform.ServerId.DEPRECATED_SERVER_ID_LENGTH; +import static org.sonar.core.platform.ServerId.NOT_UUID_DATASET_ID_LENGTH; +import static org.sonar.core.platform.ServerId.SPLIT_CHARACTER; +import static org.sonar.core.platform.ServerId.UUID_DATASET_ID_LENGTH; +import static org.sonar.core.platform.ServerId.Format.DEPRECATED; +import static org.sonar.core.platform.ServerId.Format.NO_DATABASE_ID; +import static org.sonar.core.platform.ServerId.Format.WITH_DATABASE_ID; + +@RunWith(DataProviderRunner.class) +public class ServerIdTest { + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void parse_throws_NPE_if_argument_is_null() { + expectedException.expect(NullPointerException.class); + + ServerId.parse(null); + } + + @Test + @UseDataProvider("emptyAfterTrim") + public void parse_throws_IAE_if_parameter_is_empty_after_trim(String serverId) { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("serverId can't be empty"); + + ServerId.parse(serverId); + } + + @DataProvider + public static Object[][] emptyAfterTrim() { + return new Object[][] { + {""}, + {" "}, + {" "} + }; + } + + @Test + @UseDataProvider("wrongFormatWithDatabaseId") + public void parse_throws_IAE_if_split_char_is_at_wrong_position(String emptyDatabaseId) { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Unrecognized serverId format. Parts have wrong length"); + + ServerId.parse(emptyDatabaseId); + } + + @DataProvider + public static Object[][] wrongFormatWithDatabaseId() { + String onlySplitChar = repeat(SPLIT_CHARACTER + "", DATABASE_ID_LENGTH); + String startWithSplitChar = SPLIT_CHARACTER + randomAlphabetic(DATABASE_ID_LENGTH - 1); + + Stream<String> databaseIds = Stream.of( + UuidFactoryImpl.INSTANCE.create(), + randomAlphabetic(NOT_UUID_DATASET_ID_LENGTH), + randomAlphabetic(UUID_DATASET_ID_LENGTH), + repeat(SPLIT_CHARACTER + "", NOT_UUID_DATASET_ID_LENGTH), + repeat(SPLIT_CHARACTER + "", UUID_DATASET_ID_LENGTH)); + + return databaseIds + .flatMap(datasetId -> Stream.of( + startWithSplitChar + SPLIT_CHARACTER + datasetId, + onlySplitChar + SPLIT_CHARACTER + datasetId, + startWithSplitChar + randomAlphabetic(1) + datasetId, + onlySplitChar + randomAlphabetic(1) + datasetId)) + .flatMap(serverId -> Stream.of( + serverId, + " " + serverId, + " " + serverId)) + .map(t -> new Object[] {t}) + .toArray(Object[][]::new); + } + + @Test + public void parse_parses_deprecated_format_serverId() { + String deprecated = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); + + ServerId serverId = ServerId.parse(deprecated); + + assertThat(serverId.getFormat()).isEqualTo(DEPRECATED); + assertThat(serverId.getDatasetId()).isEqualTo(deprecated); + assertThat(serverId.getDatabaseId()).isEmpty(); + assertThat(serverId.toString()).isEqualTo(deprecated); + } + + @Test + @UseDataProvider("validOldFormatServerIds") + public void parse_parses_no_databaseId_format_serverId(String noDatabaseId) { + ServerId serverId = ServerId.parse(noDatabaseId); + + assertThat(serverId.getFormat()).isEqualTo(NO_DATABASE_ID); + assertThat(serverId.getDatasetId()).isEqualTo(noDatabaseId); + assertThat(serverId.getDatabaseId()).isEmpty(); + assertThat(serverId.toString()).isEqualTo(noDatabaseId); + } + + @DataProvider + public static Object[][] validOldFormatServerIds() { + return new Object[][] { + {UuidFactoryImpl.INSTANCE.create()}, + {randomAlphabetic(NOT_UUID_DATASET_ID_LENGTH)}, + {repeat(SPLIT_CHARACTER + "", NOT_UUID_DATASET_ID_LENGTH)}, + {randomAlphabetic(UUID_DATASET_ID_LENGTH)}, + {repeat(SPLIT_CHARACTER + "", UUID_DATASET_ID_LENGTH)} + }; + } + + @Test + @UseDataProvider("validServerIdWithDatabaseId") + public void parse_parses_serverId_with_database_id(String databaseId, String datasetId) { + String rawServerId = databaseId + SPLIT_CHARACTER + datasetId; + + ServerId serverId = ServerId.parse(rawServerId); + + assertThat(serverId.getFormat()).isEqualTo(WITH_DATABASE_ID); + assertThat(serverId.getDatasetId()).isEqualTo(datasetId); + assertThat(serverId.getDatabaseId()).contains(databaseId); + assertThat(serverId.toString()).isEqualTo(rawServerId); + } + + @DataProvider + public static Object[][] validServerIdWithDatabaseId() { + return new Object[][] { + {randomAlphabetic(DATABASE_ID_LENGTH), randomAlphabetic(NOT_UUID_DATASET_ID_LENGTH)}, + {randomAlphabetic(DATABASE_ID_LENGTH), randomAlphabetic(UUID_DATASET_ID_LENGTH)}, + {randomAlphabetic(DATABASE_ID_LENGTH), repeat(SPLIT_CHARACTER + "", NOT_UUID_DATASET_ID_LENGTH)}, + {randomAlphabetic(DATABASE_ID_LENGTH), repeat(SPLIT_CHARACTER + "", UUID_DATASET_ID_LENGTH)}, + {randomAlphabetic(DATABASE_ID_LENGTH), UuidFactoryImpl.INSTANCE.create()}, + }; + } + + @Test + public void parse_does_not_support_deprecated_server_id_with_database_id() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("serverId does not have a supported length"); + + ServerId.parse(randomAlphabetic(DATABASE_ID_LENGTH) + SPLIT_CHARACTER + randomAlphabetic(DEPRECATED_SERVER_ID_LENGTH)); + } + + @Test + public void of_throws_NPE_if_datasetId_is_null() { + expectedException.expect(NullPointerException.class); + + ServerId.of(randomAlphabetic(DATABASE_ID_LENGTH), null); + } + + @Test + public void of_throws_IAE_if_datasetId_is_empty() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Illegal datasetId length (0)"); + + ServerId.of(randomAlphabetic(DATABASE_ID_LENGTH), ""); + } + + @Test + public void of_throws_IAE_if_databaseId_is_empty() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Illegal databaseId length (0)"); + + ServerId.of("", randomAlphabetic(UUID_DATASET_ID_LENGTH)); + } + + @Test + @UseDataProvider("datasetIdSupportedLengths") + public void of_accepts_null_databaseId(int datasetIdLength) { + String datasetId = randomAlphabetic(datasetIdLength); + ServerId serverId = ServerId.of(null, datasetId); + + assertThat(serverId.getDatabaseId()).isEmpty(); + assertThat(serverId.getDatasetId()).isEqualTo(datasetId); + } + + @Test + @UseDataProvider("illegalDatabaseIdLengths") + public void of_throws_IAE_if_databaseId_length_is_not_8(int illegalDatabaseIdLengths) { + String databaseId = randomAlphabetic(illegalDatabaseIdLengths); + String datasetId = randomAlphabetic(UUID_DATASET_ID_LENGTH); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Illegal databaseId length (" + illegalDatabaseIdLengths + ")"); + + ServerId.of(databaseId, datasetId); + } + + @DataProvider + public static Object[][] illegalDatabaseIdLengths() { + return IntStream.range(1, 8 + new Random().nextInt(5)) + .filter(i -> i != DATABASE_ID_LENGTH) + .mapToObj(i -> new Object[] {i}) + .toArray(Object[][]::new); + } + + @Test + @UseDataProvider("illegalDatasetIdLengths") + public void of_throws_IAE_if_datasetId_length_is_not_8(int illegalDatasetIdLengths) { + String datasetId = randomAlphabetic(illegalDatasetIdLengths); + String databaseId = randomAlphabetic(DATABASE_ID_LENGTH); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Illegal datasetId length (" + illegalDatasetIdLengths + ")"); + + ServerId.of(databaseId, datasetId); + } + + @DataProvider + public static Object[][] illegalDatasetIdLengths() { + return IntStream.range(1, UUID_DATASET_ID_LENGTH + new Random().nextInt(5)) + .filter(i -> i != UUID_DATASET_ID_LENGTH) + .filter(i -> i != NOT_UUID_DATASET_ID_LENGTH) + .filter(i -> i != DEPRECATED_SERVER_ID_LENGTH) + .mapToObj(i -> new Object[] {i}) + .toArray(Object[][]::new); + } + + @Test + @UseDataProvider("datasetIdSupportedLengths") + public void equals_is_based_on_databaseId_and_datasetId(int datasetIdLength) { + String databaseId = randomAlphabetic(DATABASE_ID_LENGTH - 1) + 'a'; + String otherDatabaseId = randomAlphabetic(DATABASE_ID_LENGTH - 1) + 'b'; + String datasetId = randomAlphabetic(datasetIdLength - 1) + 'a'; + String otherDatasetId = randomAlphabetic(datasetIdLength - 1) + 'b'; + + ServerId newServerId = ServerId.of(databaseId, datasetId); + assertThat(newServerId).isEqualTo(newServerId); + assertThat(newServerId).isEqualTo(ServerId.of(databaseId, datasetId)); + assertThat(newServerId).isNotEqualTo(new Object()); + assertThat(newServerId).isNotEqualTo(null); + assertThat(newServerId).isNotEqualTo(ServerId.of(otherDatabaseId, datasetId)); + assertThat(newServerId).isNotEqualTo(ServerId.of(databaseId, otherDatasetId)); + assertThat(newServerId).isNotEqualTo(ServerId.of(otherDatabaseId, otherDatasetId)); + + ServerId oldServerId = ServerId.parse(datasetId); + assertThat(oldServerId).isEqualTo(oldServerId); + assertThat(oldServerId).isEqualTo(ServerId.parse(datasetId)); + assertThat(oldServerId).isNotEqualTo(ServerId.parse(otherDatasetId)); + assertThat(oldServerId).isNotEqualTo(ServerId.of(databaseId, datasetId)); + } + + @Test + @UseDataProvider("datasetIdSupportedLengths") + public void hashcode_is_based_on_databaseId_and_datasetId(int datasetIdLength) { + String databaseId = randomAlphabetic(DATABASE_ID_LENGTH - 1) + 'a'; + String otherDatabaseId = randomAlphabetic(DATABASE_ID_LENGTH - 1) + 'b'; + String datasetId = randomAlphabetic(datasetIdLength - 1) + 'a'; + String otherDatasetId = randomAlphabetic(datasetIdLength - 1) + 'b'; + + ServerId newServerId = ServerId.of(databaseId, datasetId); + assertThat(newServerId.hashCode()).isEqualTo(newServerId.hashCode()); + assertThat(newServerId.hashCode()).isEqualTo(ServerId.of(databaseId, datasetId).hashCode()); + assertThat(newServerId.hashCode()).isNotEqualTo(new Object().hashCode()); + assertThat(newServerId.hashCode()).isNotEqualTo(null); + assertThat(newServerId.hashCode()).isNotEqualTo(ServerId.of(otherDatabaseId, datasetId).hashCode()); + assertThat(newServerId.hashCode()).isNotEqualTo(ServerId.of(databaseId, otherDatasetId).hashCode()); + assertThat(newServerId.hashCode()).isNotEqualTo(ServerId.of(otherDatabaseId, otherDatasetId).hashCode()); + + ServerId oldServerId = ServerId.parse(datasetId); + assertThat(oldServerId.hashCode()).isEqualTo(oldServerId.hashCode()); + assertThat(oldServerId.hashCode()).isEqualTo(ServerId.parse(datasetId).hashCode()); + assertThat(oldServerId.hashCode()).isNotEqualTo(ServerId.parse(otherDatasetId).hashCode()); + assertThat(oldServerId.hashCode()).isNotEqualTo(ServerId.of(databaseId, datasetId).hashCode()); + } + + @DataProvider + public static Object[][] datasetIdSupportedLengths() { + return new Object[][] { + {ServerId.NOT_UUID_DATASET_ID_LENGTH}, + {UUID_DATASET_ID_LENGTH}, + }; + } +} |