]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5628 Project settings taken into account during the data cleaning of project...
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Wed, 5 Nov 2014 08:37:51 +0000 (09:37 +0100)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Wed, 5 Nov 2014 16:21:28 +0000 (17:21 +0100)
16 files changed:
server/sonar-server/src/main/java/org/sonar/server/computation/DataCleanerStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/InvalidatePreviewCacheStep.java
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/java/org/sonar/server/properties/ProjectSettings.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/properties/ProjectSettingsFactory.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/DataCleanerStepTest.java
server/sonar-server/src/test/java/org/sonar/server/properties/ProjectSettingsFactoryTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/properties/ProjectSettingsTest.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/ProjectPurgeTask.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleaner.java
sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java
sonar-core/src/main/java/org/sonar/core/properties/PropertiesMapper.java
sonar-core/src/main/resources/org/sonar/core/properties/PropertiesMapper.xml
sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/ProjectPurgeTaskTest.java [new file with mode: 0644]
sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java
sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/selectProjectPropertiesByResourceId.xml [new file with mode: 0644]

index 8532f486ad1e316365843780a29b3c0e11eabc48..de0337ed078fbd327ca733414cb84dcda152a5d9 100644 (file)
@@ -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
index e71ee278627384f0668c4495a13076b89b5d78c0..e08f1bfe1fdeba9dd55ad1026186e1b91ff304f8 100644 (file)
@@ -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()));
   }
index acca81fec93440b95ca6afcada1333fd5fab8133..e0a069fe6c23ce91e39247900b8d5679059a20fe 100644 (file)
@@ -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 (file)
index 0000000..6d7b1a9
--- /dev/null
@@ -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<String, String> properties;
+
+  public ProjectSettings(Settings settings, Map<String, String> 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 (file)
index 0000000..e3b8481
--- /dev/null
@@ -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<PropertyDto> propertyList = dao.selectProjectProperties(projectId);
+
+    return new ProjectSettings(settings, getPropertyMap(propertyList));
+  }
+
+  @VisibleForTesting
+  Map<String, String> getPropertyMap(List<PropertyDto> propertyDtoList) {
+    Map<String, String> propertyMap = Maps.newHashMap();
+    for (PropertyDto property : propertyDtoList) {
+      String key = property.getKey();
+      String value = property.getValue();
+      propertyMap.put(key, value);
+    }
+
+    return propertyMap;
+  }
+}
index 6e90a48cff87d6e40980d2b8e368637b2c9685c0..bb0589c1217f2f82472c6341339a82fb161ad817 100644 (file)
@@ -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 (file)
index 0000000..d5d97c8
--- /dev/null
@@ -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<String, String> propertyMap = sut.getPropertyMap(Lists.<PropertyDto>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<String, String> 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 (file)
index 0000000..144592e
--- /dev/null
@@ -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.<String, String>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<String, String> 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<String, String> 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<String, String> 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<String, String> 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<String, String> 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<String, String> 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<String, String> 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 (file)
index 0000000..a2e1fe8
--- /dev/null
@@ -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);
+    }
+  }
+}
index a7f8a482533392b9c4a8d602ee3a2d72da9fff45..bbb6a222ba82a9962addbdfc0752f62055ce9b7a 100644 (file)
@@ -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());
   }
 
index e7a13b39f2620dcc670c56be83395e2928b06d14..6865434dfcde3f4142a02aad063d5d04039193db 100644 (file)
@@ -99,6 +99,15 @@ public class PropertiesDao implements BatchComponent, ServerComponent, DaoCompon
     return session.getMapper(PropertiesMapper.class).selectProjectProperties(resourceKey);
   }
 
+  public List<PropertyDto> selectProjectProperties(long resourceId) {
+    SqlSession session = mybatis.openSession(false);
+    try {
+      return session.getMapper(PropertiesMapper.class).selectProjectPropertiesByResourceId(resourceId);
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+  }
+
   public List<PropertyDto> selectProjectProperties(String resourceKey) {
     SqlSession session = mybatis.openSession(false);
     try {
index 6cdf28160aff8c3737ae5e66eaeb4513c59172fd..10f298b53c32e1b4e8e7f8f57af86111e62a708f 100644 (file)
@@ -35,6 +35,8 @@ public interface PropertiesMapper {
 
   List<PropertyDto> selectProjectProperties(String resourceKey);
 
+  List<PropertyDto> selectProjectPropertiesByResourceId(Long resourceId);
+
   List<PropertyDto> selectSetOfResourceProperties(@Param("rId") Long projectId, @Param("propKeys") List<String> 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);
 }
index 250b49a361babff557cc9ffe29fc1e3e6c76441e..dedc3d19362b272979ac2f95d3d1797530246c50 100644 (file)
     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
     <if test="componentKey != null">
       OR P.resource_id in (select id from projects where kee=#{componentKey})
     </if>
     )
   </select>
 
-  <select id="selectGlobalProperties" resultType="Property" >
+  <select id="selectGlobalProperties" resultType="Property">
     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>
 
-  <select id="selectProjectProperties" parameterType="String" resultType="Property" >
+  <select id="selectProjectProperties" parameterType="String" resultType="Property">
     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}
+  </select>
+
+  <select id="selectProjectPropertiesByResourceId" parameterType="Long" resultType="Property">
+    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=#{resourceId} and p.user_id is null
   </select>
 
   <select id="selectSetOfResourceProperties" parameterType="map" resultType="Property">
     </where>
   </select>
 
-  <update id="update" parameterType="Property" >
+  <update id="update" parameterType="Property">
     update properties set text_value = #{value} where id = #{id}
   </update>
 
-  <insert id="insert" parameterType="Property" useGeneratedKeys="false" >
+  <insert id="insert" parameterType="Property" useGeneratedKeys="false">
     INSERT INTO properties (prop_key, resource_id, user_id, text_value)
     VALUES (#{key}, #{resourceId}, #{userId}, #{value})
   </insert>
 
-  <delete id="deleteProjectProperty" parameterType="map" >
+  <delete id="deleteProjectProperty" parameterType="map">
     delete from properties where prop_key=#{key} and resource_id=#{rId} and user_id is null
   </delete>
 
-  <delete id="deleteProjectProperties" parameterType="map" >
+  <delete id="deleteProjectProperties" parameterType="map">
     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>
 
-  <delete id="deleteGlobalProperty" parameterType="string" >
+  <delete id="deleteGlobalProperty" parameterType="string">
     delete from properties where prop_key=#{id} and resource_id is null and user_id is null
   </delete>
 
-  <delete id="deleteGlobalProperties" >
+  <delete id="deleteGlobalProperties">
     delete from properties where resource_id is null and user_id is null
   </delete>
 
-  <delete id="deleteAllProperties" parameterType="string" >
+  <delete id="deleteAllProperties" parameterType="string">
     delete from properties where prop_key=#{id}
   </delete>
 
-  <update id="renamePropertyKey" parameterType="map" >
+  <update id="renamePropertyKey" parameterType="map">
     update properties set prop_key=#{newKey} where prop_key=#{oldKey}
   </update>
 
-  <update id="updateProperties" parameterType="map" >
+  <update id="updateProperties" parameterType="map">
     update properties set text_value=#{newValue} where text_value LIKE #{oldValue} and prop_key=#{key}
   </update>
 
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 (file)
index 0000000..5b96c00
--- /dev/null
@@ -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));
+  }
+}
index 807bfec1775ea5750a263bbfe7ed601ff7b2233d..454ba2829c2344e3ded3266ae40a3dfa0073d9f0 100644 (file)
@@ -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<PropertyDto> 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 (file)
index 0000000..52fec85
--- /dev/null
@@ -0,0 +1,22 @@
+<dataset>
+
+  <!-- global -->
+  <properties id="1" prop_key="global.one" text_value="one" resource_id="[null]" user_id="[null]"/>
+  <properties id="2" prop_key="global.two" text_value="two" resource_id="[null]" user_id="[null]"/>
+
+  <!-- struts -->
+  <properties id="3" prop_key="struts.one" text_value="one" resource_id="10" user_id="[null]"/>
+
+  <!-- commons -->
+  <properties id="4" prop_key="commonslang.one" text_value="two" resource_id="11" user_id="[null]"/>
+
+  <!-- user -->
+  <properties id="5" prop_key="user.one" text_value="one" resource_id="[null]" user_id="100"/>
+  <properties id="6" prop_key="user.two" text_value="two" resource_id="10" user_id="[null]"/>
+
+  <properties id="7" prop_key="commonslang.one" text_value="one" resource_id="12" user_id="[null]"/>
+
+  <projects id="10" uuid="A" kee="org.struts:struts"/>
+  <projects id="11" uuid="B" kee="org.apache:commons-lang"/>
+  <projects id="12" uuid="C" kee="other"/>
+</dataset>