From 0e9234234737081869c10678aa4468e172156b25 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Wed, 5 Nov 2014 09:37:51 +0100 Subject: [PATCH] SONAR-5628 Project settings taken into account during the data cleaning of project analysis --- .../server/computation/DataCleanerStep.java | 23 +-- .../InvalidatePreviewCacheStep.java | 4 +- .../server/platform/ServerComponents.java | 4 + .../server/properties/ProjectSettings.java | 72 ++++++++++ .../properties/ProjectSettingsFactory.java | 60 ++++++++ .../computation/DataCleanerStepTest.java | 19 ++- .../ProjectSettingsFactoryTest.java | 76 ++++++++++ .../properties/ProjectSettingsTest.java | 135 ++++++++++++++++++ .../dbcleaner/ProjectPurgeTask.java | 77 ++++++++++ .../period/DefaultPeriodCleaner.java | 7 +- .../sonar/core/properties/PropertiesDao.java | 9 ++ .../core/properties/PropertiesMapper.java | 4 +- .../core/properties/PropertiesMapper.xml | 40 +++--- .../dbcleaner/ProjectPurgeTaskTest.java | 88 ++++++++++++ .../core/properties/PropertiesDaoTest.java | 17 ++- .../selectProjectPropertiesByResourceId.xml | 22 +++ 16 files changed, 612 insertions(+), 45 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/properties/ProjectSettings.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/properties/ProjectSettingsFactory.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/properties/ProjectSettingsFactoryTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/properties/ProjectSettingsTest.java create mode 100644 sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/ProjectPurgeTask.java create mode 100644 sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/ProjectPurgeTaskTest.java create mode 100644 sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/selectProjectPropertiesByResourceId.xml diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/DataCleanerStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/DataCleanerStep.java index 8532f486ad1..de0337ed078 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/DataCleanerStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/DataCleanerStep.java @@ -23,33 +23,34 @@ package org.sonar.server.computation; import org.sonar.api.config.Settings; import org.sonar.core.component.ComponentDto; import org.sonar.core.computation.db.AnalysisReportDto; -import org.sonar.core.computation.dbcleaner.DefaultPurgeTask; +import org.sonar.core.computation.dbcleaner.ProjectPurgeTask; import org.sonar.core.persistence.DbSession; +import org.sonar.server.properties.ProjectSettingsFactory; import org.sonar.core.purge.PurgeConfiguration; import org.sonar.server.issue.index.IssueIndex; -import java.util.Date; +import static org.sonar.core.purge.PurgeConfiguration.newDefaultPurgeConfiguration; public class DataCleanerStep implements ComputationStep { - private final DefaultPurgeTask purgeTask; + private final ProjectPurgeTask purgeTask; private final IssueIndex issueIndex; - private final Settings settings; + private final ProjectSettingsFactory projectSettingsFactory; - public DataCleanerStep(DefaultPurgeTask purgeTask, IssueIndex issueIndex, Settings settings) { + public DataCleanerStep(ProjectSettingsFactory projectSettingsFactory, ProjectPurgeTask purgeTask, IssueIndex issueIndex) { + this.projectSettingsFactory = projectSettingsFactory; this.purgeTask = purgeTask; this.issueIndex = issueIndex; - this.settings = settings; } @Override public void execute(DbSession session, AnalysisReportDto report, ComponentDto project) { Long projectId = project.getId(); - purgeTask.purge(projectId); - issueIndex.deleteClosedIssuesOfProjectBefore(project.uuid(), dateBeforeWhichDeleteClosedIssues(projectId)); - } - private Date dateBeforeWhichDeleteClosedIssues(Long resourceId) { - return PurgeConfiguration.newDefaultPurgeConfiguration(resourceId, settings).maxLiveDateOfClosedIssues(); + Settings settings = projectSettingsFactory.newProjectSettings(projectId); + PurgeConfiguration purgeConfiguration = newDefaultPurgeConfiguration(projectId, settings); + + purgeTask.purge(purgeConfiguration, settings); + issueIndex.deleteClosedIssuesOfProjectBefore(project.uuid(), purgeConfiguration.maxLiveDateOfClosedIssues()); } @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/InvalidatePreviewCacheStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/InvalidatePreviewCacheStep.java index e71ee278627..e08f1bfe1fd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/InvalidatePreviewCacheStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/InvalidatePreviewCacheStep.java @@ -37,10 +37,10 @@ public class InvalidatePreviewCacheStep implements ComputationStep { @Override public void execute(DbSession session, AnalysisReportDto analysisReportDto, ComponentDto project) { - propertiesDao.setProperty(updatedProjectPreviewCacheProperty(project)); + propertiesDao.setProperty(newProjectPreviewCacheProperty(project)); } - private PropertyDto updatedProjectPreviewCacheProperty(ComponentDto project) { + private PropertyDto newProjectPreviewCacheProperty(ComponentDto project) { return new PropertyDto().setKey(PreviewCache.SONAR_PREVIEW_CACHE_LAST_UPDATE_KEY).setResourceId(project.getId()) .setValue(String.valueOf(System.currentTimeMillis())); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index acca81fec93..e0a069fe6c2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -38,6 +38,7 @@ import org.sonar.api.utils.UriReader; import org.sonar.api.utils.internal.TempFolderCleaner; import org.sonar.core.component.SnapshotPerspectives; import org.sonar.core.computation.dbcleaner.DefaultPurgeTask; +import org.sonar.core.computation.dbcleaner.ProjectPurgeTask; import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner; import org.sonar.core.config.CorePropertyDefinitions; import org.sonar.core.config.Logback; @@ -55,6 +56,7 @@ import org.sonar.core.permission.PermissionFacade; import org.sonar.core.persistence.*; import org.sonar.core.preview.PreviewCache; import org.sonar.core.profiling.Profiling; +import org.sonar.server.properties.ProjectSettingsFactory; import org.sonar.core.purge.PurgeProfiler; import org.sonar.core.qualitygate.db.ProjectQgateAssociationDao; import org.sonar.core.qualitygate.db.QualityGateConditionDao; @@ -608,6 +610,8 @@ class ServerComponents { pico.addSingleton(AnalysisReportHistorySearchAction.class); pico.addSingleton(DefaultPeriodCleaner.class); pico.addSingleton(DefaultPurgeTask.class); + pico.addSingleton(ProjectPurgeTask.class); + pico.addSingleton(ProjectSettingsFactory.class); for (Object components : level4AddedComponents) { pico.addSingleton(components); diff --git a/server/sonar-server/src/main/java/org/sonar/server/properties/ProjectSettings.java b/server/sonar-server/src/main/java/org/sonar/server/properties/ProjectSettings.java new file mode 100644 index 00000000000..6d7b1a9854e --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/properties/ProjectSettings.java @@ -0,0 +1,72 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.properties; + +import org.apache.commons.lang.StringUtils; +import org.sonar.api.config.Settings; + +import java.util.Map; + +public class ProjectSettings extends Settings { + private final Settings settings; + private final Map properties; + + public ProjectSettings(Settings settings, Map properties) { + this.settings = settings; + this.properties = properties; + } + + @Override + public String getString(String key) { + String value = get(key); + if (value == null) { + return settings.getString(key); + } + + return value; + } + + @Override + public boolean getBoolean(String key) { + String value = get(key); + if (value == null) { + return settings.getBoolean(key); + } + + return StringUtils.isNotEmpty(value) && Boolean.parseBoolean(value); + } + + @Override + public int getInt(String key) { + String value = get(key); + if (value == null) { + return settings.getInt(key); + } else if (StringUtils.isNotEmpty(value)) { + return Integer.parseInt(value); + } else { + return 0; + } + } + + private String get(String key) { + return properties.get(key); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/properties/ProjectSettingsFactory.java b/server/sonar-server/src/main/java/org/sonar/server/properties/ProjectSettingsFactory.java new file mode 100644 index 00000000000..e3b84813f30 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/properties/ProjectSettingsFactory.java @@ -0,0 +1,60 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.properties; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Maps; +import org.sonar.api.ServerComponent; +import org.sonar.api.config.Settings; +import org.sonar.core.properties.PropertiesDao; +import org.sonar.core.properties.PropertyDto; + +import java.util.List; +import java.util.Map; + +public class ProjectSettingsFactory implements ServerComponent { + + private final PropertiesDao dao; + private final Settings settings; + + public ProjectSettingsFactory(Settings settings, PropertiesDao dao) { + this.dao = dao; + this.settings = settings; + } + + public Settings newProjectSettings(long projectId) { + List propertyList = dao.selectProjectProperties(projectId); + + return new ProjectSettings(settings, getPropertyMap(propertyList)); + } + + @VisibleForTesting + Map getPropertyMap(List propertyDtoList) { + Map propertyMap = Maps.newHashMap(); + for (PropertyDto property : propertyDtoList) { + String key = property.getKey(); + String value = property.getValue(); + propertyMap.put(key, value); + } + + return propertyMap; + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/DataCleanerStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/DataCleanerStepTest.java index 6e90a48cff8..bb0589c1217 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/DataCleanerStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/DataCleanerStepTest.java @@ -25,8 +25,11 @@ import org.junit.Test; import org.sonar.api.config.Settings; import org.sonar.core.component.ComponentDto; import org.sonar.core.computation.db.AnalysisReportDto; -import org.sonar.core.computation.dbcleaner.DefaultPurgeTask; +import org.sonar.core.computation.dbcleaner.ProjectPurgeTask; import org.sonar.core.persistence.DbSession; +import org.sonar.server.properties.ProjectSettings; +import org.sonar.server.properties.ProjectSettingsFactory; +import org.sonar.core.purge.PurgeConfiguration; import org.sonar.server.issue.index.IssueIndex; import java.util.Date; @@ -37,17 +40,20 @@ import static org.mockito.Mockito.*; public class DataCleanerStepTest { private DataCleanerStep sut; - private DefaultPurgeTask purgeTask; + private ProjectPurgeTask purgeTask; private IssueIndex issueIndex; private Settings settings; + private ProjectSettingsFactory projectSettingsFactory; @Before public void before() { - this.purgeTask = mock(DefaultPurgeTask.class); + this.purgeTask = mock(ProjectPurgeTask.class); this.issueIndex = mock(IssueIndex.class); - this.settings = mock(Settings.class); + this.settings = mock(ProjectSettings.class); + this.projectSettingsFactory = mock(ProjectSettingsFactory.class); + when(projectSettingsFactory.newProjectSettings(anyLong())).thenReturn(settings); - this.sut = new DataCleanerStep(purgeTask, issueIndex, settings); + this.sut = new DataCleanerStep(projectSettingsFactory, purgeTask, issueIndex); } @Test @@ -57,7 +63,8 @@ public class DataCleanerStepTest { sut.execute(mock(DbSession.class), report, project); - verify(purgeTask).purge(any(Long.class)); + verify(projectSettingsFactory).newProjectSettings(anyLong()); + verify(purgeTask).purge(any(PurgeConfiguration.class), any(Settings.class)); verify(issueIndex).deleteClosedIssuesOfProjectBefore(anyString(), any(Date.class)); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/properties/ProjectSettingsFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/properties/ProjectSettingsFactoryTest.java new file mode 100644 index 00000000000..d5d97c8dc38 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/properties/ProjectSettingsFactoryTest.java @@ -0,0 +1,76 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.properties; + +import com.google.common.collect.Lists; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.config.Settings; +import org.sonar.core.properties.PropertiesDao; +import org.sonar.core.properties.PropertyDto; + +import java.util.Map; + +import static com.google.common.collect.Lists.newArrayList; +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class ProjectSettingsFactoryTest { + + private ProjectSettingsFactory sut; + private Settings settings; + private PropertiesDao dao; + + @Before + public void before() { + this.settings = mock(Settings.class); + this.dao = mock(PropertiesDao.class); + + this.sut = new ProjectSettingsFactory(settings, dao); + } + + @Test + public void newProjectSettings_returns_a_ProjectSettings() throws Exception { + Settings projectSettings = sut.newProjectSettings(1L); + + assertThat(projectSettings).isInstanceOf(ProjectSettings.class); + } + + @Test + public void transform_empty_list_into_empty_map() throws Exception { + Map propertyMap = sut.getPropertyMap(Lists.newArrayList()); + + assertThat(propertyMap).isEmpty(); + } + + @Test + public void transform_list_of_properties_in_map_key_value() throws Exception { + PropertyDto property1 = new PropertyDto().setKey("1").setValue("val1"); + PropertyDto property2 = new PropertyDto().setKey("2").setValue("val2"); + PropertyDto property3 = new PropertyDto().setKey("3").setValue("val3"); + + Map propertyMap = sut.getPropertyMap(newArrayList(property1, property2, property3)); + + assertThat(propertyMap.get("1")).isEqualTo("val1"); + assertThat(propertyMap.get("2")).isEqualTo("val2"); + assertThat(propertyMap.get("3")).isEqualTo("val3"); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/properties/ProjectSettingsTest.java b/server/sonar-server/src/test/java/org/sonar/server/properties/ProjectSettingsTest.java new file mode 100644 index 00000000000..144592e6d09 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/properties/ProjectSettingsTest.java @@ -0,0 +1,135 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.properties; + +import com.google.common.collect.Maps; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.config.Settings; + +import java.util.HashMap; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; + +public class ProjectSettingsTest { + + private ProjectSettings sut; + + private Settings settings; + + @Before + public void before() throws Exception { + this.settings = mock(Settings.class); + } + + @Test + public void call_global_settings_method_when_no_project_specific_settings() throws Exception { + this.sut = new ProjectSettings(settings, Maps.newHashMap()); + + sut.getInt("anyKey"); + sut.getBoolean("anyKey"); + sut.getString("anyKey"); + + verify(settings, times(1)).getBoolean(anyString()); + verify(settings, times(1)).getInt(anyString()); + verify(settings, times(1)).getString(anyString()); + } + + @Test(expected = NumberFormatException.class) + public void getInt_property_throws_exception_when_value_is_not_formatted_correctly() throws Exception { + HashMap properties = Maps.newHashMap(); + properties.put("intKey", "wrongIntValue"); + this.sut = new ProjectSettings(settings, properties); + + sut.getInt("intKey"); + } + + @Test + public void getInt_property_return_0_when_empty_property() throws Exception { + HashMap properties = Maps.newHashMap(); + properties.put("intKey", ""); + this.sut = new ProjectSettings(settings, properties); + + int value = sut.getInt("intKey"); + + assertThat(value).isEqualTo(0); + } + + @Test + public void getInt_property_return_the_int_value() throws Exception { + HashMap properties = Maps.newHashMap(); + properties.put("intKey", "123"); + this.sut = new ProjectSettings(settings, properties); + + int value = sut.getInt("intKey"); + + assertThat(value).isEqualTo(123); + } + + @Test + public void getString_returns_String_property() throws Exception { + HashMap properties = Maps.newHashMap(); + properties.put("stringKey", "stringValue"); + this.sut = new ProjectSettings(settings, properties); + + String value = sut.getString("stringKey"); + + assertThat(value).isEqualTo("stringValue"); + } + + @Test + public void getBoolean_returns_exception_when_value_is_not_formatted_correctly() throws Exception { + HashMap properties = Maps.newHashMap(); + properties.put("boolKey", "wronglyFormattedBoolean"); + this.sut = new ProjectSettings(settings, properties); + + boolean key = sut.getBoolean("boolKey"); + + assertThat(key).isFalse(); + } + + @Test + public void getBoolean_returns_false_when_value_is_empty() throws Exception { + HashMap properties = Maps.newHashMap(); + properties.put("boolKey", ""); + this.sut = new ProjectSettings(settings, properties); + + boolean key = sut.getBoolean("boolKey"); + + assertThat(key).isFalse(); + } + + @Test + public void getBoolean_returns_true_when_value_is_true_ignoring_case() throws Exception { + HashMap properties = Maps.newHashMap(); + properties.put("boolKey1", "true"); + properties.put("boolKey2", "True"); + this.sut = new ProjectSettings(settings, properties); + + boolean key1 = sut.getBoolean("boolKey1"); + boolean key2 = sut.getBoolean("boolKey2"); + + assertThat(key1).isTrue(); + assertThat(key2).isTrue(); + } +} diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/ProjectPurgeTask.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/ProjectPurgeTask.java new file mode 100644 index 00000000000..a2e1fe842ec --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/ProjectPurgeTask.java @@ -0,0 +1,77 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.computation.dbcleaner; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.CoreProperties; +import org.sonar.api.ServerComponent; +import org.sonar.api.config.Settings; +import org.sonar.api.utils.TimeUtils; +import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner; +import org.sonar.core.purge.PurgeConfiguration; +import org.sonar.core.purge.PurgeDao; +import org.sonar.core.purge.PurgeProfiler; + +public class ProjectPurgeTask implements ServerComponent { + private static final Logger LOG = LoggerFactory.getLogger(ProjectPurgeTask.class); + private final PurgeProfiler profiler; + private final PurgeDao purgeDao; + private final DefaultPeriodCleaner periodCleaner; + + public ProjectPurgeTask(PurgeDao purgeDao, DefaultPeriodCleaner periodCleaner, PurgeProfiler profiler) { + this.purgeDao = purgeDao; + this.periodCleaner = periodCleaner; + this.profiler = profiler; + } + + public ProjectPurgeTask purge(PurgeConfiguration configuration, Settings settings) { + long start = System.currentTimeMillis(); + profiler.reset(); + cleanHistoricalData(configuration.rootProjectId(), settings); + doPurge(configuration); + if (settings.getBoolean(CoreProperties.PROFILING_LOG_PROPERTY)) { + long duration = System.currentTimeMillis() - start; + LOG.info("\n -------- Profiling for purge: " + TimeUtils.formatDuration(duration) + " --------\n"); + profiler.dump(duration, LOG); + LOG.info("\n -------- End of profiling for purge --------\n"); + } + return this; + } + + private void cleanHistoricalData(long resourceId, Settings settings) { + try { + periodCleaner.clean(resourceId, settings); + } catch (Exception e) { + // purge errors must no fail the batch + LOG.error("Fail to clean historical data [id=" + resourceId + "]", e); + } + } + + private void doPurge(PurgeConfiguration configuration) { + try { + purgeDao.purge(configuration); + } catch (Exception e) { + // purge errors must no fail the report analysis + LOG.error("Fail to purge data [id=" + configuration.rootProjectId() + "]", e); + } + } +} diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleaner.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleaner.java index a7f8a482533..bbb6a222ba8 100644 --- a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleaner.java +++ b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleaner.java @@ -25,7 +25,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.ServerExtension; import org.sonar.api.config.Settings; -import org.sonar.api.resources.Project; import org.sonar.api.task.TaskExtension; import org.sonar.api.utils.DateUtils; import org.sonar.core.purge.PurgeDao; @@ -45,11 +44,11 @@ public class DefaultPeriodCleaner implements TaskExtension, ServerExtension { this.settings = settings; } - public void purge(Project project, int projectSnapshotId) { - clean(project.getId()); + public void clean(long projectId) { + clean(projectId, settings); } - public void clean(long projectId) { + public void clean(long projectId, Settings settings) { doClean(projectId, new Filters(settings).all()); } diff --git a/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java b/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java index e7a13b39f26..6865434dfcd 100644 --- a/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java +++ b/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java @@ -99,6 +99,15 @@ public class PropertiesDao implements BatchComponent, ServerComponent, DaoCompon return session.getMapper(PropertiesMapper.class).selectProjectProperties(resourceKey); } + public List selectProjectProperties(long resourceId) { + SqlSession session = mybatis.openSession(false); + try { + return session.getMapper(PropertiesMapper.class).selectProjectPropertiesByResourceId(resourceId); + } finally { + MyBatis.closeQuietly(session); + } + } + public List selectProjectProperties(String resourceKey) { SqlSession session = mybatis.openSession(false); try { diff --git a/sonar-core/src/main/java/org/sonar/core/properties/PropertiesMapper.java b/sonar-core/src/main/java/org/sonar/core/properties/PropertiesMapper.java index 6cdf28160af..10f298b53c3 100644 --- a/sonar-core/src/main/java/org/sonar/core/properties/PropertiesMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/properties/PropertiesMapper.java @@ -35,6 +35,8 @@ public interface PropertiesMapper { List selectProjectProperties(String resourceKey); + List selectProjectPropertiesByResourceId(Long resourceId); + List selectSetOfResourceProperties(@Param("rId") Long projectId, @Param("propKeys") List propertyKeys); PropertyDto selectByKey(PropertyDto key); @@ -57,5 +59,5 @@ public interface PropertiesMapper { void renamePropertyKey(@Param("oldKey") String oldKey, @Param("newKey") String newKey); - void updateProperties(@Param("key") String key, @Param("oldValue")String oldValue, @Param("newValue") String newValue); + void updateProperties(@Param("key") String key, @Param("oldValue") String oldValue, @Param("newValue") String newValue); } diff --git a/sonar-core/src/main/resources/org/sonar/core/properties/PropertiesMapper.xml b/sonar-core/src/main/resources/org/sonar/core/properties/PropertiesMapper.xml index 250b49a361b..dedc3d19362 100644 --- a/sonar-core/src/main/resources/org/sonar/core/properties/PropertiesMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/properties/PropertiesMapper.xml @@ -20,23 +20,29 @@ FROM properties P, users U WHERE P.user_id = U.id AND P.prop_key = #{propKey} AND P.text_value LIKE 'true' AND ( - P.resource_id is null + P.resource_id is null OR P.resource_id in (select id from projects where kee=#{componentKey}) ) - select p.id as id, p.prop_key as "key", p.text_value as value, p.resource_id as resourceId, p.user_id as userId from properties p where p.resource_id is null and p.user_id is null - select p.id as id, p.prop_key as "key", p.text_value as value, p.resource_id as resourceId, p.user_id as userId from properties p, projects r - where p.resource_id=r.id and p.user_id is null and r.kee=#{id} + where p.resource_id=r.id and p.user_id is null and r.kee=#{resourceKey} + + + - + update properties set text_value = #{value} where id = #{id} - + INSERT INTO properties (prop_key, resource_id, user_id, text_value) VALUES (#{key}, #{resourceId}, #{userId}, #{value}) - + delete from properties where prop_key=#{key} and resource_id=#{rId} and user_id is null - + DELETE FROM properties WHERE - prop_key=#{key} - AND text_value LIKE #{value} - AND resource_id IS NOT NULL - AND user_id IS NULL + prop_key=#{key} + AND text_value LIKE #{value} + AND resource_id IS NOT NULL + AND user_id IS NULL - + delete from properties where prop_key=#{id} and resource_id is null and user_id is null - + delete from properties where resource_id is null and user_id is null - + delete from properties where prop_key=#{id} - + update properties set prop_key=#{newKey} where prop_key=#{oldKey} - + update properties set text_value=#{newValue} where text_value LIKE #{oldValue} and prop_key=#{key} diff --git a/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/ProjectPurgeTaskTest.java b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/ProjectPurgeTaskTest.java new file mode 100644 index 00000000000..5b96c00a150 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/ProjectPurgeTaskTest.java @@ -0,0 +1,88 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.computation.dbcleaner; + +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.sonar.api.CoreProperties; +import org.sonar.api.config.Settings; +import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner; +import org.sonar.core.purge.PurgeConfiguration; +import org.sonar.core.purge.PurgeDao; +import org.sonar.core.purge.PurgeProfiler; + +import static org.mockito.Mockito.*; + +public class ProjectPurgeTaskTest { + + private ProjectPurgeTask sut; + private PurgeDao dao; + private PurgeProfiler profiler; + private DefaultPeriodCleaner periodCleaner; + + @Before + public void before() throws Exception { + this.dao = mock(PurgeDao.class); + this.profiler = mock(PurgeProfiler.class); + this.periodCleaner = mock(DefaultPeriodCleaner.class); + + this.sut = new ProjectPurgeTask(dao, periodCleaner, profiler); + } + + @Test + public void no_profiling_when_property_is_false() throws Exception { + Settings settings = mock(Settings.class); + when(settings.getBoolean(CoreProperties.PROFILING_LOG_PROPERTY)).thenReturn(false); + + sut.purge(mock(PurgeConfiguration.class), settings); + + verify(profiler, never()).dump(anyLong(), any(Logger.class)); + } + + @Test + public void profiling_when_property_is_true() throws Exception { + Settings settings = mock(Settings.class); + when(settings.getBoolean(CoreProperties.PROFILING_LOG_PROPERTY)).thenReturn(true); + + sut.purge(mock(PurgeConfiguration.class), settings); + + verify(profiler, times(1)).dump(anyLong(), any(Logger.class)); + } + + @Test + public void if_dao_purge_fails_it_should_not_interrupt_program_execution() throws Exception { + when(dao.purge(any(PurgeConfiguration.class))).thenThrow(NullPointerException.class); + + sut.purge(mock(PurgeConfiguration.class), mock(Settings.class)); + + verify(dao, times(1)).purge(any(PurgeConfiguration.class)); + } + + @Test + public void if_profiler_cleaning_fails_it_should_not_interrupt_program_execution() throws Exception { + doThrow(NullPointerException.class).when(periodCleaner).clean(anyLong(), any(Settings.class)); + + sut.purge(mock(PurgeConfiguration.class), mock(Settings.class)); + + verify(periodCleaner, times(1)).clean(anyLong(), any(Settings.class)); + } +} diff --git a/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java b/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java index 807bfec1775..454ba2829c2 100644 --- a/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java @@ -36,12 +36,10 @@ import static org.junit.Assert.assertThat; public class PropertiesDaoTest extends AbstractDaoTestCase { - private DbSession session; - - private PropertiesDao dao; - @Rule public ExpectedException thrown = ExpectedException.none(); + private DbSession session; + private PropertiesDao dao; @Before public void createDao() { @@ -143,6 +141,17 @@ public class PropertiesDaoTest extends AbstractDaoTestCase { assertThat(first.getValue(), is("one")); } + @Test + public void selectProjectPropertiesByResourceId() { + setupData("selectProjectPropertiesByResourceId"); + + List properties = dao.selectProjectProperties(10L); + + assertThat(properties.size(), is(2)); + assertThat(properties).onProperty("key").containsOnly("struts.one", "user.two"); + assertThat(properties).onProperty("value").containsOnly("one", "two"); + } + @Test public void selectProjectProperty() { setupData("selectProjectProperties"); diff --git a/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/selectProjectPropertiesByResourceId.xml b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/selectProjectPropertiesByResourceId.xml new file mode 100644 index 00000000000..52fec852a8d --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/selectProjectPropertiesByResourceId.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + -- 2.39.5