]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5849 Performance issue of Project Referentials WS for project with many modules
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 30 Dec 2014 17:57:46 +0000 (18:57 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 30 Dec 2014 17:57:54 +0000 (18:57 +0100)
18 files changed:
server/sonar-server/src/main/java/org/sonar/server/batch/ProjectReferentialsLoader.java
server/sonar-server/src/main/java/org/sonar/server/component/persistence/ComponentDao.java
server/sonar-server/src/main/java/org/sonar/server/component/persistence/SnapshotDao.java
server/sonar-server/src/test/java/org/sonar/server/component/SnapshotTesting.java
server/sonar-server/src/test/java/org/sonar/server/component/persistence/ComponentDaoTest.java
server/sonar-server/src/test/java/org/sonar/server/component/persistence/SnapshotDaoTest.java
server/sonar-server/src/test/resources/org/sonar/server/component/persistence/SnapshotDaoTest/modules.xml [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/ProjectRefentialsComponentDto.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java
sonar-core/src/main/java/org/sonar/core/component/db/SnapshotMapper.java
sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.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/component/db/ComponentMapper.xml
sonar-core/src/main/resources/org/sonar/core/component/db/SnapshotMapper.xml
sonar-core/src/main/resources/org/sonar/core/properties/PropertiesMapper.xml
sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java
sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/select_children_module_properties.xml [new file with mode: 0644]

index 1200ec007dce40193a298b0f50ab6bfe051adb2e..39ab3e198bd9d1f139055403cd9d55d8f73a04d3 100644 (file)
@@ -20,7 +20,9 @@
 
 package org.sonar.server.batch;
 
+import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.resources.Language;
 import org.sonar.api.resources.Languages;
@@ -28,6 +30,7 @@ import org.sonar.batch.protocol.input.ProjectReferentials;
 import org.sonar.core.UtcDateUtils;
 import org.sonar.core.component.AuthorizedComponentDto;
 import org.sonar.core.component.ComponentDto;
+import org.sonar.core.component.ProjectRefentialsComponentDto;
 import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.MyBatis;
@@ -75,22 +78,31 @@ public class ProjectReferentialsLoader implements ServerComponent {
     DbSession session = dbClient.openSession(false);
     try {
       ProjectReferentials ref = new ProjectReferentials();
-      String projectKey = null;
+      String projectKey = query.getModuleKey();
       AuthorizedComponentDto module = dbClient.componentDao().getNullableAuthorizedComponentByKey(query.getModuleKey(), session);
       // Current project/module can be null when analysing a new project
       if (module != null) {
         ComponentDto project = dbClient.componentDao().getNullableRootProjectByKey(query.getModuleKey(), session);
+
         // Can be null if the given project is a provisioned one
         if (project != null) {
           if (!project.key().equals(module.key())) {
             addSettings(ref, module.getKey(), getSettingsFromParents(module.key(), hasScanPerm, session));
+            projectKey = project.key();
+          }
+
+          List<PropertyDto> moduleSettings = dbClient.propertiesDao().selectProjectProperties(query.getModuleKey(), session);
+          List<ProjectRefentialsComponentDto> moduleChildren = dbClient.componentDao().findChildrenModulesFromModule(session, query.getModuleKey());
+          List<PropertyDto> moduleChildrenSettings = newArrayList();
+          if (!moduleChildren.isEmpty()) {
+            moduleChildrenSettings = dbClient.propertiesDao().findChildrenModuleProperties(query.getModuleKey(), session);
           }
-          projectKey = project.key();
-          addSettingsToChildrenModules(ref, query.getModuleKey(), Maps.<String, String>newHashMap(), hasScanPerm, session);
+          TreeModuleSettings treeModuleSettings = new TreeModuleSettings(moduleChildren, moduleChildrenSettings, module, moduleSettings);
+
+          addSettingsToChildrenModules(ref, query.getModuleKey(), Maps.<String, String>newHashMap(), treeModuleSettings, hasScanPerm, session);
         } else {
           // Add settings of the provisioned project
           addSettings(ref, query.getModuleKey(), getPropertiesMap(dbClient.propertiesDao().selectProjectProperties(query.getModuleKey(), session), hasScanPerm));
-          projectKey = query.getModuleKey();
         }
       }
 
@@ -122,15 +134,16 @@ public class ProjectReferentialsLoader implements ServerComponent {
     }
   }
 
-  private void addSettingsToChildrenModules(ProjectReferentials ref, String projectKey, Map<String, String> parentProperties, boolean hasScanPerm, DbSession session) {
+  private void addSettingsToChildrenModules(ProjectReferentials ref, String moduleKey, Map<String, String> parentProperties, TreeModuleSettings treeModuleSettings,
+                                            boolean hasScanPerm, DbSession session) {
     Map<String, String> currentParentProperties = newHashMap();
     currentParentProperties.putAll(parentProperties);
-    currentParentProperties.putAll(getPropertiesMap(dbClient.propertiesDao().selectProjectProperties(projectKey, session), hasScanPerm));
-    addSettings(ref, projectKey, currentParentProperties);
+    currentParentProperties.putAll(getPropertiesMap(treeModuleSettings.findModuleSettings(moduleKey), hasScanPerm));
+    addSettings(ref, moduleKey, currentParentProperties);
 
-    for (ComponentDto module : dbClient.componentDao().findModulesByProject(projectKey, session)) {
-      addSettings(ref, module.key(), currentParentProperties);
-      addSettingsToChildrenModules(ref, module.key(), currentParentProperties, hasScanPerm, session);
+    for (ComponentDto childModule : treeModuleSettings.findChildrenModule(moduleKey)) {
+      addSettings(ref, childModule.getKey(), currentParentProperties);
+      addSettingsToChildrenModules(ref, childModule.getKey(), currentParentProperties, treeModuleSettings, hasScanPerm, session);
     }
   }
 
@@ -140,7 +153,7 @@ public class ProjectReferentialsLoader implements ServerComponent {
     }
   }
 
-  private Map<String, String> getPropertiesMap(List<PropertyDto> propertyDtos, boolean hasScanPerm) {
+  private Map<String, String> getPropertiesMap(List<? extends PropertyDto> propertyDtos, boolean hasScanPerm) {
     Map<String, String> properties = newHashMap();
     for (PropertyDto propertyDto : propertyDtos) {
       String key = propertyDto.getKey();
@@ -219,4 +232,43 @@ public class ProjectReferentialsLoader implements ServerComponent {
         "Please contact your SonarQube administrator.");
     }
   }
+
+  private static class TreeModuleSettings {
+
+    private Map<String, Long> moduleIdsByKey;
+    private Multimap<Long, PropertyDto> propertiesByModuleId;
+    private Multimap<String, ProjectRefentialsComponentDto> moduleChildrenByModuleKey;
+
+    private TreeModuleSettings(List<ProjectRefentialsComponentDto> moduleChildren, List<PropertyDto> moduleChildrenSettings, AuthorizedComponentDto module, List<PropertyDto> moduleSettings) {
+      propertiesByModuleId = ArrayListMultimap.create();
+      for (PropertyDto settings : moduleChildrenSettings) {
+        propertiesByModuleId.put(settings.getResourceId(), settings);
+      }
+      propertiesByModuleId.putAll(module.getId(), moduleSettings);
+      moduleIdsByKey = newHashMap();
+      for (ProjectRefentialsComponentDto componentDto : moduleChildren) {
+        moduleIdsByKey.put(componentDto.key(), componentDto.getId());
+      }
+      moduleIdsByKey.put(module.key(), module.getId());
+      moduleChildrenByModuleKey = ArrayListMultimap.create();
+      for (ProjectRefentialsComponentDto componentDto : moduleChildren) {
+        String parentModuleKey = componentDto.getParentModuleKey();
+        if (parentModuleKey != null) {
+          moduleChildrenByModuleKey.put(parentModuleKey, componentDto);
+        } else {
+          moduleChildrenByModuleKey.put(module.key(), componentDto);
+        }
+      }
+    }
+
+    private  List<PropertyDto> findModuleSettings(String moduleKey) {
+      Long moduleId = moduleIdsByKey.get(moduleKey);
+      return newArrayList(propertiesByModuleId.get(moduleId));
+    }
+
+    private List<? extends ComponentDto> findChildrenModule(String moduleKey) {
+      return newArrayList(moduleChildrenByModuleKey.get(moduleKey));
+    }
+
+  }
 }
index edcbe529d1603ffd141fdbebe1fe2a743818c01e..d866a4d1763608f10c9d15606a3f83049dcaf99d 100644 (file)
@@ -23,6 +23,7 @@ import org.sonar.api.ServerComponent;
 import org.sonar.api.utils.System2;
 import org.sonar.core.component.AuthorizedComponentDto;
 import org.sonar.core.component.ComponentDto;
+import org.sonar.core.component.ProjectRefentialsComponentDto;
 import org.sonar.core.component.db.ComponentMapper;
 import org.sonar.core.persistence.DaoComponent;
 import org.sonar.core.persistence.DbSession;
@@ -30,6 +31,7 @@ import org.sonar.server.db.BaseDao;
 import org.sonar.server.exceptions.NotFoundException;
 
 import javax.annotation.CheckForNull;
+
 import java.util.List;
 
 /**
@@ -84,6 +86,10 @@ public class ComponentDao extends BaseDao<ComponentMapper, ComponentDto, String>
     return mapper(session).findModulesByProject(projectKey);
   }
 
+  public List<ProjectRefentialsComponentDto> findChildrenModulesFromModule(DbSession session, String moduleKey) {
+    return mapper(session).findChildrenModulesFromModule(moduleKey);
+  }
+
   @CheckForNull
   public AuthorizedComponentDto getNullableAuthorizedComponentById(Long id, DbSession session) {
     return mapper(session).selectAuthorizedComponentById(id);
index 0d2db6552b0ab531b5c30556f41fa468035be8e0..2f3060b910948a0687aab0568ca3f6183a4a1fda 100644 (file)
@@ -66,6 +66,13 @@ public class SnapshotDao extends BaseDao<SnapshotMapper, SnapshotDto, Long> impl
     return mapper(session).selectSnapshotAndChildrenOfScope(snapshot.getId(), Scopes.PROJECT);
   }
 
+  /**
+   * Return all snapshots children (not returning itself) from a module key
+   */
+  public List<SnapshotDto> findChildrenModulesFromModule(DbSession session, String moduleKey) {
+    return mapper(session).selectChildrenModulesFromModule(moduleKey);
+  }
+
   public int updateSnapshotAndChildrenLastFlagAndStatus(DbSession session, SnapshotDto snapshot, boolean isLast, String status) {
     Long rootId = snapshot.getId();
     String path = snapshot.getPath() + snapshot.getId() + ".%";
index 8785d3db8b802928effd3b5a2b51e498968b228d..1b174c9a11f1d02725613803210fb9240cfe3aba 100644 (file)
@@ -24,6 +24,8 @@ import org.sonar.api.utils.DateUtils;
 import org.sonar.core.component.ComponentDto;
 import org.sonar.core.component.SnapshotDto;
 
+import java.util.Date;
+
 public class SnapshotTesting {
 
   /**
@@ -39,7 +41,9 @@ public class SnapshotTesting {
       .setQualifier(component.qualifier())
       .setScope(component.scope())
       .setParentId(parentSnapshot.getId())
-      .setLast(true);
+      .setPath(parentSnapshot.getPath() == null ? Long.toString(parentSnapshot.getId()) + "." : parentSnapshot.getPath() + Long.toString(parentSnapshot.getId()) + ".")
+      .setLast(true)
+      .setBuildDate(new Date());
   }
 
   public static SnapshotDto createForProject(ComponentDto project) {
@@ -49,7 +53,9 @@ public class SnapshotTesting {
       .setStatus(SnapshotDto.STATUS_PROCESSED)
       .setQualifier(project.qualifier())
       .setScope(project.scope())
-      .setLast(true);
+      .setPath("")
+      .setLast(true)
+      .setBuildDate(new Date());
   }
 
   public static SnapshotDto defaultSnapshot() {
index 5296d905c942780895eb6848450d214b2778fa83..5c58dc4fae729f48e1fa650e67b8f704cacc3729 100644 (file)
@@ -26,6 +26,7 @@ import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.System2;
 import org.sonar.core.component.AuthorizedComponentDto;
 import org.sonar.core.component.ComponentDto;
+import org.sonar.core.component.ProjectRefentialsComponentDto;
 import org.sonar.core.persistence.AbstractDaoTestCase;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.server.exceptions.NotFoundException;
@@ -230,6 +231,27 @@ public class ComponentDaoTest extends AbstractDaoTestCase {
     dao.getAuthorizedComponentByKey("unknown", session);
   }
 
+  @Test
+  public void find_children_modules_from_module() throws Exception {
+    setupData("multi-modules");
+
+    // From root project
+    List<ProjectRefentialsComponentDto> modules = dao.findChildrenModulesFromModule(session, "org.struts:struts");
+    assertThat(modules).hasSize(2);
+    assertThat(modules).onProperty("id").containsOnly(2L, 3L);
+    assertThat(modules).onProperty("parentModuleKey").containsOnly("org.struts:struts", "org.struts:struts-core");
+
+    // From module
+    modules = dao.findChildrenModulesFromModule(session, "org.struts:struts-core");
+    assertThat(modules).hasSize(1);
+    assertThat(modules).onProperty("id").containsOnly(3L);
+    assertThat(modules).onProperty("parentModuleKey").containsOnly("org.struts:struts-core");
+
+    // From sub module
+    modules = dao.findChildrenModulesFromModule(session, "org.struts:struts-data");
+    assertThat(modules).isEmpty();
+  }
+
   @Test
   public void insert() {
     when(system2.now()).thenReturn(DateUtils.parseDate("2014-06-18").getTime());
index e77aa0ac088882e4a93b2d3e20c578af309d9788..4780e00324cfa7c9df5e13a9774f70f71eef74a9 100644 (file)
@@ -212,6 +212,26 @@ public class SnapshotDaoTest extends AbstractDaoTestCase {
     assertThat(snapshots).onProperty("last").containsOnly(false);
   }
 
+  @Test
+  public void find_children_modules() {
+    setupData("modules");
+
+    // From root project
+    List<SnapshotDto> snapshots = sut.findChildrenModulesFromModule(session, "org.struts:struts");
+    assertThat(snapshots).hasSize(2);
+    assertThat(snapshots).onProperty("id").containsOnly(2L, 3L);
+    assertThat(snapshots).onProperty("last").containsOnly(true);
+
+    // From module
+    snapshots = sut.findChildrenModulesFromModule(session, "org.struts:struts-core");
+    assertThat(snapshots).hasSize(1);
+    assertThat(snapshots).onProperty("id").containsOnly(3L);
+
+    // From sub module
+    snapshots = sut.findChildrenModulesFromModule(session, "org.struts:struts-data");
+    assertThat(snapshots).isEmpty();
+  }
+
   @Test
   public void is_last_snapshot_when_no_previous_snapshot() {
     SnapshotDto snapshot = defaultSnapshot();
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/persistence/SnapshotDaoTest/modules.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/persistence/SnapshotDaoTest/modules.xml
new file mode 100644 (file)
index 0000000..2e13e2a
--- /dev/null
@@ -0,0 +1,85 @@
+<dataset>
+
+  <!-- root project -->
+  <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+            description="the description" long_name="Apache Struts"
+            enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" />
+  <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+             version="[null]" path=""/>
+  <snapshots id="10" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             status="P" islast="[false]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-01 13:58:00.00" build_date="2008-12-01 13:58:00.00"
+             version="[null]" path=""/>
+
+  <!-- module -->
+  <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core"
+            scope="PRJ" qualifier="BRC" long_name="Struts Core"
+            description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" />
+  <snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+             version="[null]" path="1."/>
+
+  <!-- sub module -->
+  <projects id="3" root_id="1" kee="org.struts:struts-data" name="Struts Data"
+            scope="PRJ" qualifier="BRC" long_name="Struts Data"
+            description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" />
+  <snapshots id="3" project_id="3" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+             version="[null]" path="1.2."/>
+
+  <!-- directory -->
+  <projects long_name="org.struts" id="4" scope="DIR" qualifier="DIR" kee="org.struts:struts-core:src/org/struts"
+            name="src/org/struts" root_id="3"
+            description="[null]"
+            enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/org/struts" />
+  <snapshots id="4" project_id="4" parent_snapshot_id="3" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+             version="[null]" path="1.2.3."/>
+
+  <!-- file -->
+  <projects long_name="org.struts.RequestContext" id="5" scope="FIL" qualifier="FIL" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+            name="RequestContext.java" root_id="3"
+            description="[null]"
+            enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="src/org/struts/RequestContext.java" />
+
+  <snapshots id="5" project_id="5" parent_snapshot_id="4" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+             version="[null]" path="1.2.3.4."/>
+
+</dataset>
diff --git a/sonar-core/src/main/java/org/sonar/core/component/ProjectRefentialsComponentDto.java b/sonar-core/src/main/java/org/sonar/core/component/ProjectRefentialsComponentDto.java
new file mode 100644 (file)
index 0000000..e531993
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.component;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+public class ProjectRefentialsComponentDto extends ComponentDto {
+
+  private String parentModuleKey;
+
+  @CheckForNull
+  public String getParentModuleKey() {
+    return parentModuleKey;
+  }
+
+  public void setParentModuleKey(@Nullable String parentModuleKey) {
+    this.parentModuleKey = parentModuleKey;
+  }
+}
index 58cfdba2b5da5f5ac4a06a8a62a7c3694caa7241..7c78b798cba2e5d25ce5878d55b07d283d8379f8 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.core.component.db;
 import org.apache.ibatis.annotations.Param;
 import org.sonar.core.component.AuthorizedComponentDto;
 import org.sonar.core.component.ComponentDto;
+import org.sonar.core.component.ProjectRefentialsComponentDto;
 
 import javax.annotation.CheckForNull;
 
@@ -49,6 +50,11 @@ public interface ComponentMapper {
    */
   List<ComponentDto> findModulesByProject(@Param("projectKey") String projectKey);
 
+  /**
+   * Return all modules children (not returning itself) from a module key
+   */
+  List<ProjectRefentialsComponentDto> findChildrenModulesFromModule(@Param("moduleKey") String moduleKey);
+
   long countById(long id);
 
   @CheckForNull
index ba98cfb11fb3f61a8816102bb1375885dcf7f204..608a4b99c9cd62fa6b3cbcd25cdd8d5c82658f41 100644 (file)
@@ -43,6 +43,8 @@ public interface SnapshotMapper {
 
   List<SnapshotDto> selectSnapshotAndChildrenOfScope(@Param(value = "snapshot") Long resourceId, @Param(value = "scope") String scope);
 
+  List<SnapshotDto> selectChildrenModulesFromModule(@Param(value = "moduleKey") String moduleKey);
+
   int updateSnapshotAndChildrenLastFlagAndStatus(@Param(value = "root") Long rootId, @Param(value = "pathRootId") Long pathRootId,
                                                  @Param(value = "path") String path, @Param(value = "isLast") boolean isLast, @Param(value = "status") String status);
 
index 0c348064e44cfd64ce4116d4d26b85653a09c156..98602acfa1002ec4724282b0a8e2fcec1370fef0 100644 (file)
@@ -36,6 +36,7 @@ import org.sonar.core.activity.db.ActivityMapper;
 import org.sonar.core.cluster.WorkQueue;
 import org.sonar.core.component.AuthorizedComponentDto;
 import org.sonar.core.component.ComponentDto;
+import org.sonar.core.component.ProjectRefentialsComponentDto;
 import org.sonar.core.component.db.ComponentMapper;
 import org.sonar.core.component.db.SnapshotMapper;
 import org.sonar.core.config.Logback;
@@ -107,6 +108,7 @@ public class MyBatis implements BatchComponent, ServerComponent {
     loadAlias(conf, "ActiveDashboard", ActiveDashboardDto.class);
     loadAlias(conf, "Author", AuthorDto.class);
     loadAlias(conf, "Component", ComponentDto.class);
+    loadAlias(conf, "ProjectRefentialsComponent", ProjectRefentialsComponentDto.class);
     loadAlias(conf, "AuthorizedComponent", AuthorizedComponentDto.class);
     loadAlias(conf, "Dashboard", DashboardDto.class);
     loadAlias(conf, "Dependency", DependencyDto.class);
index e7a13b39f2620dcc670c56be83395e2928b06d14..11c4a80dd0f6a9dfea8ce12cef695145097a51b5 100644 (file)
@@ -108,6 +108,10 @@ public class PropertiesDao implements BatchComponent, ServerComponent, DaoCompon
     }
   }
 
+  public List<PropertyDto> findChildrenModuleProperties(String moduleKey, SqlSession session) {
+    return session.getMapper(PropertiesMapper.class).selectChildrenModuleProperties(moduleKey);
+  }
+
   public PropertyDto selectProjectProperty(long resourceId, String propertyKey) {
     SqlSession session = mybatis.openSession(false);
     PropertiesMapper mapper = session.getMapper(PropertiesMapper.class);
index 6cdf28160aff8c3737ae5e66eaeb4513c59172fd..fe53da10a6a041180240973c4a521dd6f3f30d87 100644 (file)
@@ -41,6 +41,8 @@ public interface PropertiesMapper {
 
   List<PropertyDto> selectByQuery(@Param("query") PropertyQuery query);
 
+  List<PropertyDto> selectChildrenModuleProperties(@Param("moduleKey") String moduleKey);
+
   void update(PropertyDto property);
 
   void insert(PropertyDto property);
index 452bebb9fcf7c646d6f563c5017cdec5987d1794..3d91df7d6658b79db6919f888ef739b3fea15195 100644 (file)
     </where>
   </select>
 
+  <select id="findChildrenModulesFromModule" parameterType="String" resultType="ProjectRefentialsComponent">
+    SELECT <include refid="componentColumns"/>, parent.kee as parentModuleKey
+    FROM projects p
+    INNER JOIN snapshots s ON s.project_id=p.id AND s.islast=${_true}
+    INNER JOIN (<include refid="org.sonar.core.component.db.SnapshotMapper.selectChildrenModulesFromModuleQuery" />) snapshotModules on snapshotModules.resourceId=p.id
+    LEFT OUTER JOIN snapshots parent_snapshot on parent_snapshot.id = snapshotModules.parentId
+    LEFT OUTER JOIN projects parent on parent.id = parent_snapshot.project_id
+  </select>
+
   <sql id="insertColumns">
     (kee, name, long_name, qualifier, scope, language, root_id, path, created_at)
   </sql>
index c0112194bcf1759a74e9a15790b876f5581be5e4..0aeb697a2c32170c3d59ece7dd23e1a58fc198d3 100644 (file)
     AND (s.id = #{snapshot} or s.root_snapshot_id = #{snapshot})
   </select>
 
+  <select id="selectChildrenModulesFromModule" parameterType="map" resultType="Snapshot">
+    <include refid="selectChildrenModulesFromModuleQuery" />
+  </select>
+
+  <sql id="selectChildrenModulesFromModuleQuery">
+    SELECT <include refid="org.sonar.core.component.db.SnapshotMapper.snapshotColumns"/>
+    FROM snapshots s
+    INNER JOIN snapshots root_snapshot ON root_snapshot.id = s.root_snapshot_id AND root_snapshot.islast = ${_true}
+    INNER JOIN snapshots current_snapshot ON current_snapshot.root_project_id = root_snapshot.project_id AND s.islast = ${_true}
+    INNER JOIN projects module ON module.id = current_snapshot.project_id AND module.enabled = ${_true} AND module.kee = #{moduleKey}
+    <where>
+      AND s.islast = ${_true}
+      AND s.scope = 'PRJ'
+      AND <choose>
+      <when test="_databaseId == 'mssql'">
+        s.path LIKE current_snapshot.path + CAST(current_snapshot.id AS varchar(15)) + '.%'
+      </when>
+      <when test="_databaseId == 'mysql'">
+        s.path LIKE concat(current_snapshot.path, current_snapshot.id, '.%')
+      </when>
+      <otherwise>
+        s.path LIKE current_snapshot.path || current_snapshot.id || '.%'
+      </otherwise>
+    </choose>
+    </where>
+  </sql>
+
   <sql id="insertColumns">
     (parent_snapshot_id, root_snapshot_id, root_project_id, project_id, created_at, build_date, status, purge_status,
     islast, scope, qualifier, version, path, depth,
index 250b49a361babff557cc9ffe29fc1e3e6c76441e..416b5c1f1eba2099302a7bf76a4cf4cc68b14988 100644 (file)
     <foreach item="propKey" index="index" collection="propKeys" open="(" separator="," close=")">#{propKey}</foreach>
   </select>
 
+  <select id="selectChildrenModuleProperties" parameterType="String" resultType="Property">
+    SELECT prop.id as id, prop.prop_key as "key", prop.text_value as value, prop.resource_id as resourceId, prop.user_id as userId
+    FROM properties prop
+    INNER JOIN (<include refid="org.sonar.core.component.db.SnapshotMapper.selectChildrenModulesFromModuleQuery" />) snapshotModules on snapshotModules.resourceId=prop.resource_id
+    INNER JOIN projects p on p.id = prop.resource_id
+    WHERE prop.user_id IS NULL
+  </select>
+
   <select id="selectByKey" parameterType="map" 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
index 807bfec1775ea5750a263bbfe7ed601ff7b2233d..c6110c790b858f323d9d3c63b494ef4d32536dd9 100644 (file)
@@ -143,6 +143,22 @@ public class PropertiesDaoTest extends AbstractDaoTestCase {
     assertThat(first.getValue(), is("one"));
   }
 
+  @Test
+  public void select_children_module_properties() throws Exception {
+    setupData("select_children_module_properties");
+
+    List<PropertyDto> properties = dao.findChildrenModuleProperties("org.struts:struts", session);
+    assertThat(properties.size(), is(3));
+    assertThat(properties).onProperty("key").containsOnly("core.one", "core.two", "data.one");
+    assertThat(properties).onProperty("value").containsOnly("one", "two");
+
+    properties = dao.findChildrenModuleProperties("org.struts:struts-core", session);
+    assertThat(properties.size(), is(1));
+    assertThat(properties).onProperty("key").containsOnly("data.one");
+
+    assertThat(dao.findChildrenModuleProperties("org.struts:struts-data", session).size(), is(0));
+  }
+
   @Test
   public void selectProjectProperty() {
     setupData("selectProjectProperties");
diff --git a/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/select_children_module_properties.xml b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/select_children_module_properties.xml
new file mode 100644 (file)
index 0000000..2e79434
--- /dev/null
@@ -0,0 +1,104 @@
+<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]"/>
+
+  <!-- org.struts:struts -->
+  <properties id="3" prop_key="struts.one" text_value="one" resource_id="1" user_id="[null]"/>
+
+  <!-- org.struts:struts-core -->
+  <properties id="4" prop_key="core.one" text_value="one" resource_id="2" user_id="[null]"/>
+  <properties id="7" prop_key="core.two" text_value="two" resource_id="2" user_id="[null]"/>
+
+  <!-- org.struts:struts-data -->
+  <properties id="8" prop_key="data.one" text_value="one" resource_id="3" 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="1" user_id="102"/>
+
+
+  <!-- root project -->
+  <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+            description="the description" long_name="Apache Struts"
+            enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" />
+  <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+             version="[null]" path=""/>
+  <snapshots id="10" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             status="P" islast="[false]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-01 13:58:00.00" build_date="2008-12-01 13:58:00.00"
+             version="[null]" path=""/>
+
+  <!-- module -->
+  <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core"
+            scope="PRJ" qualifier="BRC" long_name="Struts Core"
+            description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" />
+  <snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+             version="[null]" path="1."/>
+
+  <!-- sub module -->
+  <projects id="3" root_id="1" kee="org.struts:struts-data" name="Struts Data"
+            scope="PRJ" qualifier="BRC" long_name="Struts Data"
+            description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" />
+  <snapshots id="3" project_id="3" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+             version="[null]" path="1.2."/>
+
+  <!-- directory -->
+  <projects long_name="org.struts" id="4" scope="DIR" qualifier="DIR" kee="org.struts:struts-core:src/org/struts"
+            name="src/org/struts" root_id="3"
+            description="[null]"
+            enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/org/struts" />
+  <snapshots id="4" project_id="4" parent_snapshot_id="3" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+             version="[null]" path="1.2.3."/>
+
+  <!-- file -->
+  <projects long_name="org.struts.RequestContext" id="5" scope="FIL" qualifier="FIL" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+            name="RequestContext.java" root_id="3"
+            description="[null]"
+            enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="src/org/struts/RequestContext.java" />
+
+  <snapshots id="5" project_id="5" parent_snapshot_id="4" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+             version="[null]" path="1.2.3.4."/>
+
+</dataset>