--- /dev/null
+/*
+ * 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.db;
+
+import javax.annotation.concurrent.Immutable;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+@Immutable
+public final class Pagination {
+ private static final Pagination ALL = new Builder(1).andSize(Integer.MAX_VALUE);
+
+ private final int page;
+ private final int pageSize;
+
+ private Pagination(Builder builder) {
+ this.page = builder.page;
+ this.pageSize = builder.pageSize;
+ }
+
+ public static Pagination all() {
+ return ALL;
+ }
+
+ public static Builder forPage(int page) {
+ return new Builder(page);
+ }
+
+ public int getPage() {
+ return page;
+ }
+
+ public int getPageSize() {
+ return pageSize;
+ }
+
+ public int getOffset() {
+ return (page - 1) * pageSize;
+ }
+
+ public int getStartRowNumber() {
+ return getOffset() + 1;
+ }
+
+ public int getEndRowNumber() {
+ return page * pageSize;
+ }
+
+ public static final class Builder {
+ private final int page;
+ private int pageSize = 0;
+
+ public Builder(int page) {
+ checkArgument(page >= 1, "page index must be >= 1");
+ this.page = page;
+ }
+
+ public Pagination andSize(int pageSize) {
+ checkArgument(pageSize >= 1, "page size must be >= 1");
+ this.pageSize = pageSize;
+ return new Pagination(this);
+ }
+ }
+}
import org.sonar.api.utils.System2;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
+import org.sonar.db.Pagination;
-import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
import static org.sonar.db.DatabaseUtils.executeLargeInputs;
return getMapper(dbSession).selectByPermission(userId, permission);
}
- public List<OrganizationDto> selectOrganizationsWithoutLoadedTemplate(DbSession dbSession, String loadedTemplateType, int page, int pageSize) {
- checkArgument(page >= 1, "page must be >= 1");
- checkArgument(pageSize >= 1, "page size must be >= 1");
- int offset = (page - 1) * pageSize;
- return getMapper(dbSession).selectOrganizationsWithoutLoadedTemplate(loadedTemplateType, page, pageSize, offset);
+ public List<OrganizationDto> selectOrganizationsWithoutLoadedTemplate(DbSession dbSession, String loadedTemplateType, Pagination pagination) {
+ return getMapper(dbSession).selectOrganizationsWithoutLoadedTemplate(loadedTemplateType, pagination);
}
/**
import java.util.List;
import javax.annotation.CheckForNull;
import org.apache.ibatis.annotations.Param;
+import org.sonar.db.Pagination;
public interface OrganizationMapper {
void insert(@Param("organization") OrganizationDto organization);
/**
* Assuming the key of the loaded template with the specified type is an organization's UUID, select all organizations
* which does not have a row in table LOADED_TEMPLATES with the specified type.
- *
- * @param offset {@code ((#{page} - 1) * #{pageSize})}
*/
List<OrganizationDto> selectOrganizationsWithoutLoadedTemplate(@Param("loadedTemplateType") String type,
- @Param("page") int page, @Param("pageSize") int pageSize, @Param("offset") int offset);
+ @Param("pagination") Pagination pagination);
DefaultTemplates selectDefaultTemplatesByUuid(@Param("uuid") String uuid);
<include refid="sqlSelectOrganizationsWithoutLoadedTemplate"/>
order by
org.created_at asc
- limit #{pageSize} offset #{offset}
+ limit #{pagination.pageSize,jdbcType=INTEGER} offset #{pagination.offset,jdbcType=INTEGER}
</select>
<select id="selectOrganizationsWithoutLoadedTemplate" parameterType="map" resultType="Organization" databaseId="mssql">
<include refid="sqlSelectOrganizationsWithoutLoadedTemplate"/>
) as query
where
- query.number between (#{offset} + 1) and (#{page} * #{pageSize})
+ query.number between #{pagination.startRowNumber,jdbcType=INTEGER} and #{pagination.endRowNumber,jdbcType=INTEGER}
order by
query.createdAt asc
</select>
) t
) t
where
- t.rn between (#{offset} + 1) and (#{page} * #{pageSize})
+ t.rn between #{pagination.startRowNumber,jdbcType=INTEGER} and #{pagination.endRowNumber,jdbcType=INTEGER}
</select>
<sql id="sqlSelectOrganizationsWithoutLoadedTemplate">
--- /dev/null
+/*
+ * 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.db;
+
+import java.util.Random;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.Pagination.forPage;
+
+
+public class PaginationTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void all_is_page_1_with_MAX_INTEGER_page_size() {
+ Pagination pagination = Pagination.all();
+
+ assertThat(pagination.getPage()).isEqualTo(1);
+ assertThat(pagination.getPageSize()).isEqualTo(Integer.MAX_VALUE);
+ }
+
+ @Test
+ public void all_returns_a_constant() {
+ assertThat(Pagination.all()).isSameAs(Pagination.all());
+ }
+
+ @Test
+ public void forPage_fails_with_IAE_if_page_is_0() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("page index must be >= 1");
+
+ forPage(0);
+ }
+
+ @Test
+ public void forPage_fails_with_IAE_if_page_is_less_than_0() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("page index must be >= 1");
+
+ forPage(-Math.abs(new Random().nextInt()) - 1);
+ }
+
+ @Test
+ public void andSize_fails_with_IAE_if_size_is_0() {
+ Pagination.Builder builder = forPage(1);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("page size must be >= 1");
+
+ builder.andSize(0);
+ }
+
+ @Test
+ public void andSize_fails_with_IAE_if_size_is_less_than_0() {
+ Pagination.Builder builder = forPage(1);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("page size must be >= 1");
+
+ builder.andSize(-Math.abs(new Random().nextInt()) - 1);
+ }
+
+ @Test
+ public void offset_is_computed_from_page_and_size() {
+ assertThat(forPage(2).andSize(3).getOffset()).isEqualTo(3);
+ assertThat(forPage(5).andSize(3).getOffset()).isEqualTo(12);
+ assertThat(forPage(5).andSize(1).getOffset()).isEqualTo(4);
+ }
+
+ @Test
+ public void startRowNumber_is_computed_from_page_and_size() {
+ assertThat(forPage(2).andSize(3).getStartRowNumber()).isEqualTo(4);
+ assertThat(forPage(5).andSize(3).getStartRowNumber()).isEqualTo(13);
+ assertThat(forPage(5).andSize(1).getStartRowNumber()).isEqualTo(5);
+ }
+
+ @Test
+ public void endRowNumber_is_computed_from_page_and_size() {
+ assertThat(forPage(2).andSize(3).getEndRowNumber()).isEqualTo(6);
+ assertThat(forPage(5).andSize(3).getEndRowNumber()).isEqualTo(15);
+ assertThat(forPage(5).andSize(1).getEndRowNumber()).isEqualTo(5);
+ }
+}
import static org.mockito.Mockito.when;
import static org.sonar.db.organization.OrganizationQuery.newOrganizationQueryBuilder;
import static org.sonar.db.organization.OrganizationQuery.returnAll;
+import static org.sonar.db.Pagination.all;
+import static org.sonar.db.Pagination.forPage;
public class OrganizationDaoTest {
private static final long SOME_DATE = 1_200_999L;
@Test
public void selectOrganizationsWithoutLoadedTemplate_returns_empty_if_there_is_no_organization() {
- List<OrganizationDto> organizationDtos = underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "type1", 1, Integer.MAX_VALUE);
+ List<OrganizationDto> organizationDtos = underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "type1", all());
assertThat(organizationDtos).isEmpty();
}
String[] organizationUuids = IntStream.range(0, organizationCount).mapToObj(i -> "uuid_" + i).toArray(String[]::new);
Arrays.stream(organizationUuids).forEach(uuid -> dbTester.organizations().insertForUuid(uuid));
- List<OrganizationDto> organizationDtos = underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "type1", 1, Integer.MAX_VALUE);
+ List<OrganizationDto> organizationDtos = underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "type1", all());
assertThat(organizationDtos)
.extracting(OrganizationDto::getUuid)
dbTester.getDbClient().loadedTemplateDao().insert(new LoadedTemplateDto("", loadedTemplateType), dbSession);
dbTester.commit();
- List<OrganizationDto> organizationDtos = underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, loadedTemplateType, 1, Integer.MAX_VALUE);
+ List<OrganizationDto> organizationDtos = underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, loadedTemplateType, all());
assertThat(organizationDtos)
.extracting(OrganizationDto::getUuid)
when(system2.now()).thenAnswer(t -> alwaysIncreasingSystem2.now());
IntStream.range(1, 31).forEach(i -> dbTester.organizations().insertForUuid("" + i));
- assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", 1, Integer.MAX_VALUE))
+ assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", all()))
.extracting(dto -> Integer.valueOf(dto.getUuid()))
.hasSize(30)
.allMatch(i -> i > 0 && i <= 30);
- assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", 1, 30))
+ assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", forPage(1).andSize(30)))
.extracting(dto -> Integer.valueOf(dto.getUuid()))
.hasSize(30)
.allMatch(i -> i > 0 && i <= 30);
- assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", 1, 10))
+ assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", forPage(1).andSize(10)))
.extracting(dto -> Integer.valueOf(dto.getUuid()))
.hasSize(10)
.allMatch(i -> i > 0 && i <= 10);
- assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", 2, 10))
+ assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", forPage(2).andSize(10)))
.extracting(dto -> Integer.valueOf(dto.getUuid()))
.hasSize(10)
.allMatch(i -> i > 10 && i <= 20);
- assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", 5, 5))
+ assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", forPage(5).andSize(5)))
.extracting(dto -> Integer.valueOf(dto.getUuid()))
.hasSize(5)
.allMatch(i -> i > 20 && i <= 25);
- assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", 6, 5))
+ assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", forPage(6).andSize(5)))
.extracting(dto -> Integer.valueOf(dto.getUuid()))
.hasSize(5)
.allMatch(i -> i > 25 && i <= 30);
- assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", 7, 5))
+ assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", forPage(7).andSize(5)))
.isEmpty();
- assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", 2, 50))
+ assertThat(underTest.selectOrganizationsWithoutLoadedTemplate(dbSession, "foo", forPage(2).andSize(50)))
.isEmpty();
}
import org.sonar.core.util.stream.Collectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
+import org.sonar.db.Pagination;
import org.sonar.db.loadedtemplate.LoadedTemplateDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.qualityprofile.QualityProfileDto;
import static org.apache.commons.codec.binary.Hex.encodeHexString;
import static org.apache.commons.lang.StringUtils.isNotEmpty;
import static org.apache.commons.lang.StringUtils.lowerCase;
+import static org.sonar.db.Pagination.forPage;
import static org.sonar.db.loadedtemplate.LoadedTemplateDto.QUALITY_PROFILE_TYPE;
/**
private static final Logger LOGGER = Loggers.get(RegisterQualityProfiles.class);
private static final String DEFAULT_PROFILE_NAME = "Sonar way";
- private static final int PROCESSED_ORGANIZATIONS_BATCH_SIZE = 2000;
+ private static final Pagination PROCESSED_ORGANIZATIONS_BATCH_SIZE = forPage(1).andSize(2000);
private final List<ProfileDefinition> definitions;
private final DbClient dbClient;
* To be kept when no ProfileDefinition are injected
*/
public RegisterQualityProfiles(DbClient dbClient,
- QProfileFactory profileFactory, CachingRuleActivator ruleActivator, Languages languages, ActiveRuleIndexer activeRuleIndexer) {
+ QProfileFactory profileFactory, CachingRuleActivator ruleActivator, Languages languages, ActiveRuleIndexer activeRuleIndexer) {
this(dbClient, profileFactory, ruleActivator, Collections.emptyList(), languages, activeRuleIndexer);
}
public RegisterQualityProfiles(DbClient dbClient,
- QProfileFactory profileFactory, CachingRuleActivator ruleActivator,
- List<ProfileDefinition> definitions, Languages languages, ActiveRuleIndexer activeRuleIndexer) {
+ QProfileFactory profileFactory, CachingRuleActivator ruleActivator,
+ List<ProfileDefinition> definitions, Languages languages, ActiveRuleIndexer activeRuleIndexer) {
this.dbClient = dbClient;
this.profileFactory = profileFactory;
this.ruleActivator = ruleActivator;
try (DbSession session = dbClient.openSession(false)) {
List<ActiveRuleChange> changes = new ArrayList<>();
qualityProfilesByLanguage.entrySet()
- .forEach(entry -> registerPerLanguage(session, entry.getValue(), changes));
+ .forEach(entry -> registerPerLanguage(session, entry.getValue(), changes));
activeRuleIndexer.index(changes);
profiler.stopDebug();
}
private void validateAndClean(ListMultimap<String, RulesProfile> byLang) {
byLang.asMap().entrySet()
- .removeIf(entry -> {
- String language = entry.getKey();
- if (languages.get(language) == null) {
- LOGGER.info("Language {} is not installed, related Quality profiles are ignored", language);
- return true;
- }
- Collection<RulesProfile> profiles = entry.getValue();
- if (profiles.isEmpty()) {
- LOGGER.warn("No Quality profiles defined for language: {}", language);
- return true;
- }
- return false;
- });
+ .removeIf(entry -> {
+ String language = entry.getKey();
+ if (languages.get(language) == null) {
+ LOGGER.info("Language {} is not installed, related Quality profiles are ignored", language);
+ return true;
+ }
+ Collection<RulesProfile> profiles = entry.getValue();
+ if (profiles.isEmpty()) {
+ LOGGER.warn("No Quality profiles defined for language: {}", language);
+ return true;
+ }
+ return false;
+ });
}
private Map<String, List<QualityProfile>> toQualityProfilesByLanguage(ListMultimap<String, RulesProfile> rulesProfilesByLanguage) {
Map<String, List<QualityProfile.Builder>> buildersByLanguage = Multimaps.asMap(rulesProfilesByLanguage)
- .entrySet()
- .stream()
- .collect(Collectors.uniqueIndex(Map.Entry::getKey, RegisterQualityProfiles::toQualityProfileBuilders));
+ .entrySet()
+ .stream()
+ .collect(Collectors.uniqueIndex(Map.Entry::getKey, RegisterQualityProfiles::toQualityProfileBuilders));
return buildersByLanguage
- .entrySet()
- .stream()
- .filter(RegisterQualityProfiles::ensureAtMostOneDeclaredDefault)
- .collect(Collectors.uniqueIndex(Map.Entry::getKey, entry -> toQualityProfiles(entry.getValue()), buildersByLanguage.size()));
+ .entrySet()
+ .stream()
+ .filter(RegisterQualityProfiles::ensureAtMostOneDeclaredDefault)
+ .collect(Collectors.uniqueIndex(Map.Entry::getKey, entry -> toQualityProfiles(entry.getValue()), buildersByLanguage.size()));
}
/**
Map<String, QualityProfile.Builder> qualityProfileBuildersByName = new LinkedHashMap<>();
for (RulesProfile rulesProfile : rulesProfilesByLanguage.getValue()) {
qualityProfileBuildersByName.compute(
- rulesProfile.getName(),
- (name, existingBuilder) -> updateOrCreateBuilder(language, existingBuilder, rulesProfile, name));
+ rulesProfile.getName(),
+ (name, existingBuilder) -> updateOrCreateBuilder(language, existingBuilder, rulesProfile, name));
}
return ImmutableList.copyOf(qualityProfileBuildersByName.values());
}
*/
private static boolean ensureAtMostOneDeclaredDefault(Map.Entry<String, List<QualityProfile.Builder>> entry) {
Set<String> declaredDefaultProfileNames = entry.getValue().stream()
- .filter(QualityProfile.Builder::isDeclaredDefault)
- .map(QualityProfile.Builder::getName)
- .collect(Collectors.toSet());
+ .filter(QualityProfile.Builder::isDeclaredDefault)
+ .map(QualityProfile.Builder::getName)
+ .collect(Collectors.toSet());
checkState(declaredDefaultProfileNames.size() <= 1, "Several Quality profiles are flagged as default for the language %s: %s", entry.getKey(), declaredDefaultProfileNames);
return true;
}
QualityProfile.Builder builder = existingBuilder;
if (builder == null) {
builder = new QualityProfile.Builder()
- .setLanguage(language)
- .setName(name);
+ .setLanguage(language)
+ .setName(name);
}
Boolean defaultProfile = rulesProfile.getDefaultProfile();
boolean declaredDefault = defaultProfile != null && defaultProfile;
return builder
- // if there is multiple RulesProfiles with the same name, if at least one is declared default,
- // then QualityProfile is flagged as declared default
- .setDeclaredDefault(builder.declaredDefault || declaredDefault)
- .addRules(rulesProfile.getActiveRules());
+ // if there is multiple RulesProfiles with the same name, if at least one is declared default,
+ // then QualityProfile is flagged as declared default
+ .setDeclaredDefault(builder.declaredDefault || declaredDefault)
+ .addRules(rulesProfile.getActiveRules());
}
private static List<QualityProfile> toQualityProfiles(List<QualityProfile.Builder> builders) {
}
MessageDigest md5Digest = DigestUtils.getMd5Digest();
return builders.stream()
- .map(builder -> builder.build(md5Digest))
- .collect(Collectors.toList(builders.size()));
+ .map(builder -> builder.build(md5Digest))
+ .collect(Collectors.toList(builders.size()));
}
private void registerPerLanguage(DbSession session, List<QualityProfile> qualityProfiles, List<ActiveRuleChange> changes) {
private List<OrganizationDto> getOrganizationsWithoutQP(DbSession session, QualityProfile qualityProfile) {
return dbClient.organizationDao().selectOrganizationsWithoutLoadedTemplate(session,
- qualityProfile.getLoadedTemplateType(), 1, PROCESSED_ORGANIZATIONS_BATCH_SIZE);
+ qualityProfile.getLoadedTemplateType(), PROCESSED_ORGANIZATIONS_BATCH_SIZE);
}
private void registerPerQualityProfileAndOrganization(DbSession session, QualityProfile qualityProfile, OrganizationDto organization, List<ActiveRuleChange> changes) {