]> 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 14:34:49 +0000 (15:34 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 30 Dec 2014 14:54:03 +0000 (15:54 +0100)
19 files changed:
server/sonar-server/src/main/java/org/sonar/server/batch/ProjectReferentialsLoader.java
server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentDao.java
server/sonar-server/src/main/java/org/sonar/server/component/db/SnapshotDao.java
server/sonar-server/src/test/java/org/sonar/server/component/ComponentTesting.java
server/sonar-server/src/test/java/org/sonar/server/component/SnapshotTesting.java
server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentDaoTest.java
server/sonar-server/src/test/java/org/sonar/server/component/db/SnapshotDaoTest.java
server/sonar-server/src/test/resources/org/sonar/server/component/db/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 4fcbf411aea3a0fd0629da97f3392956c08adfbf..dbff7696929d52e62a29ef0c03fc55be2b8f7a92 100644 (file)
 
 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;
 import org.sonar.batch.protocol.input.ProjectReferentials;
 import org.sonar.core.UtcDateUtils;
 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;
@@ -74,22 +77,31 @@ public class ProjectReferentialsLoader implements ServerComponent {
     DbSession session = dbClient.openSession(false);
     try {
       ProjectReferentials ref = new ProjectReferentials();
-      String projectKey = null;
+      String projectKey = query.getModuleKey();
       ComponentDto module = dbClient.componentDao().getNullableByKey(session, query.getModuleKey());
       // 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();
         }
       }
 
@@ -121,15 +133,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);
     }
   }
 
@@ -139,7 +152,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();
@@ -218,4 +231,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, ComponentDto 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 c9e7f27e771faad6ca1b09e89ca80410075bf13c..69e3ec3dfa83b6dcbfed8cf8493a390fe5d90d78 100644 (file)
  * 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.component.db;
 
 import com.google.common.collect.Lists;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.utils.System2;
 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;
@@ -113,6 +115,10 @@ public class ComponentDao extends BaseDao<ComponentMapper, ComponentDto, String>
     return mapper(session).findSubProjectsByComponentUuids(keys);
   }
 
+  public List<ProjectRefentialsComponentDto> findChildrenModulesFromModule(DbSession session, String moduleKey) {
+    return mapper(session).findChildrenModulesFromModule(moduleKey);
+  }
+
   public List<ComponentDto> getByUuids(DbSession session, Collection<String> uuids) {
     if (uuids.isEmpty()) {
       return Collections.emptyList();
index 5379396bcae59d86cb551d2223621aeef945e604..213467f890318826d23b914bf070f648931a9609 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 cdc017e5aadb54fa7d0bce5bccb91cf916f8ae8a..3376773d2a4e887b0c045ce25e937e969cbede27 100644 (file)
@@ -67,7 +67,7 @@ public class ComponentTesting {
       .setUuid(Uuids.create())
       .setProjectUuid(subProjectOrProject.projectUuid())
       .setModuleUuid(subProjectOrProject.uuid())
-      .setModuleUuidPath(subProjectOrProject.moduleUuidPath() == null ? subProjectOrProject.uuid() : subProjectOrProject.moduleUuidPath() + "." + subProjectOrProject.uuid())
+      .setModuleUuidPath(subProjectOrProject.moduleUuidPath() == null ? subProjectOrProject.uuid() + "." : subProjectOrProject.moduleUuidPath() + subProjectOrProject.uuid() + ".")
       .setKey("KEY_" + uuid)
       .setName("NAME_" + uuid)
       .setLongName("LONG_NAME_" + uuid)
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 ca50790fe138d6be61f99a217813308c8e72a785..b49a20096456d35bd40cc7c2d987000edaf17f45 100644 (file)
@@ -17,6 +17,7 @@
  * 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.component.db;
 
 import org.apache.ibatis.exceptions.PersistenceException;
@@ -26,6 +27,7 @@ import org.junit.Test;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.System2;
 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;
@@ -329,6 +331,27 @@ public class ComponentDaoTest extends AbstractDaoTestCase {
     assertThat(dao.findSubProjectsByComponentUuids(session, Collections.<String>emptyList())).isEmpty();
   }
 
+  @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());
@@ -391,9 +414,9 @@ public class ComponentDaoTest extends AbstractDaoTestCase {
   @Test(expected = IllegalStateException.class)
   public void update() {
     dao.update(session, new ComponentDto()
-      .setId(1L)
-      .setKey("org.struts:struts-core:src/org/struts/RequestContext.java")
-      );
+        .setId(1L)
+        .setKey("org.struts:struts-core:src/org/struts/RequestContext.java")
+    );
   }
 
   @Test
@@ -401,9 +424,9 @@ public class ComponentDaoTest extends AbstractDaoTestCase {
     setupData("shared");
 
     dao.delete(session, new ComponentDto()
-      .setId(1L)
-      .setKey("org.struts:struts-core:src/org/struts/RequestContext.java")
-      );
+        .setId(1L)
+        .setKey("org.struts:struts-core:src/org/struts/RequestContext.java")
+    );
     session.commit();
 
     checkTable("delete", "projects");
index f799082745f503d231802b09b09697ab0f2418c2..3308afc1515b7126b91e9859910b47aee423f3c6 100644 (file)
@@ -185,6 +185,26 @@ public class SnapshotDaoTest extends AbstractDaoTestCase {
     assertThat(snapshots).onProperty("id").containsOnly(1L, 6L);
   }
 
+  @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 set_snapshot_and_children_to_false_and_status_processed() {
     setupData("snapshots");
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/db/SnapshotDaoTest/modules.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/db/SnapshotDaoTest/modules.xml
new file mode 100644 (file)
index 0000000..8bc9dec
--- /dev/null
@@ -0,0 +1,90 @@
+<dataset>
+
+  <!-- root project -->
+  <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+            uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path="[null]"
+            description="the description" long_name="Apache Struts"
+            enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" authorization_updated_at="[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"
+            uuid="EFGH" project_uuid="ABCD" module_uuid="[null]" module_uuid_path="ABCD."
+            scope="PRJ" qualifier="BRC" long_name="Struts Core"
+            description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" authorization_updated_at="[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"
+            uuid="FGHI" project_uuid="ABCD" module_uuid="EFGH" module_uuid_path="ABCD.EFGH."
+            scope="PRJ" qualifier="BRC" long_name="Struts Data"
+            description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" authorization_updated_at="[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"
+            uuid="GHIJ" project_uuid="ABCD" module_uuid="FGHI" module_uuid_path="ABCD.EFGH.FGHI."
+            name="src/org/struts" root_id="3"
+            description="[null]"
+            enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/org/struts" authorization_updated_at="[null]" />
+  <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"
+            uuid="HIJK" project_uuid="ABCD" module_uuid="GHIJ" module_uuid_path="ABCD.EFGH.FGHI.GHIJ."
+            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" authorization_updated_at="[null]" />
+
+  <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 37c0a894ae8c95316597d7c0979400dc86aee7e3..789af967e652c76f2c8b7a6051cfebb6ba79b4f8 100644 (file)
  * 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.db;
 
 import org.apache.ibatis.annotations.Param;
 import org.sonar.core.component.ComponentDto;
+import org.sonar.core.component.ProjectRefentialsComponentDto;
 
 import javax.annotation.CheckForNull;
 
@@ -72,12 +74,16 @@ public interface ComponentMapper {
    * Warning, projectId are always null
    */
   List<ComponentDto> findByUuids(@Param("uuids") Collection<String> uuids);
-
   /**
    * Return all project (PRJ/TRK) uuids
    */
   List<String> findProjectUuids();
 
+  /**
+   * Return all modules children (not returning itself) from a module key
+   */
+  List<ProjectRefentialsComponentDto> findChildrenModulesFromModule(@Param("moduleKey") String moduleKey);
+
   long countById(long id);
 
   void insert(ComponentDto rule);
index fd9d1c1188b9b7e3d9502925bedf6ac03ee646c7..31f1167a6866074acf43f33edeb43a9ebd1e17ac 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 7785eba83c8e5c2517c0ffe186c47ff4477108ce..a06112e2597ad6bacc109de437b2c4bbd1d61f70 100644 (file)
@@ -17,6 +17,7 @@
  * 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.persistence;
 
 import ch.qos.logback.classic.Level;
@@ -35,6 +36,7 @@ import org.sonar.core.activity.db.ActivityDto;
 import org.sonar.core.activity.db.ActivityMapper;
 import org.sonar.core.cluster.WorkQueue;
 import org.sonar.core.component.ComponentDto;
+import org.sonar.core.component.ProjectRefentialsComponentDto;
 import org.sonar.core.component.SnapshotDto;
 import org.sonar.core.component.db.ComponentMapper;
 import org.sonar.core.component.db.SnapshotMapper;
@@ -129,6 +131,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, "Dashboard", DashboardDto.class);
     loadAlias(conf, "Dependency", DependencyDto.class);
     loadAlias(conf, "DuplicationUnit", DuplicationUnitDto.class);
index 9ba8f2468fa90de21f5c6bad78346960c39ab7b5..4fa7f90797e99ddda8b3acd160b9974b9df55838 100644 (file)
@@ -17,6 +17,7 @@
  * 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.properties;
 
 import com.google.common.base.Preconditions;
@@ -121,6 +122,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 10f298b53c32e1b4e8e7f8f57af86111e62a708f..3fc4311c509d311fbd5860f642fb88399b5cafde 100644 (file)
@@ -17,6 +17,7 @@
  * 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.properties;
 
 import org.apache.ibatis.annotations.Param;
@@ -43,6 +44,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 685f8e42d40402b01b8b30e141b01190df768918..2af4bfc0438387b4bd6f87c60d27d8b9c56a500f 100644 (file)
     </where>
   </select>
 
+  <select id="findChildrenModulesFromModule" parameterType="String" resultType="ProjectRefentialsComponent">
+    SELECT <include refid="componentColumns"/>, parent.kee as parentModuleKey
+    FROM projects p
+    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>
+
   <select id="findProjectUuids" resultType="String">
     SELECT p.uuid
     FROM projects p
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 dedc3d19362b272979ac2f95d3d1797530246c50..d86d22e817cafd5c4975f0802c1c6477847f45f6 100644 (file)
     where p.resource_id=#{resourceId} and p.user_id is null
   </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="selectSetOfResourceProperties" 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 454ba2829c2344e3ded3266ae40a3dfa0073d9f0..12aae6b86ee3e03983ccecf4a579bfd0efe25d0c 100644 (file)
@@ -17,6 +17,7 @@
  * 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.properties;
 
 import com.google.common.collect.ImmutableMap;
@@ -152,6 +153,22 @@ public class PropertiesDaoTest extends AbstractDaoTestCase {
     assertThat(properties).onProperty("value").containsOnly("one", "two");
   }
 
+  @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..5aec43d
--- /dev/null
@@ -0,0 +1,109 @@
+<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"
+            uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path="[null]"
+            description="the description" long_name="Apache Struts"
+            enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" authorization_updated_at="[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"
+            uuid="EFGH" project_uuid="ABCD" module_uuid="[null]" module_uuid_path="ABCD."
+            scope="PRJ" qualifier="BRC" long_name="Struts Core"
+            description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" authorization_updated_at="[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"
+            uuid="FGHI" project_uuid="ABCD" module_uuid="EFGH" module_uuid_path="ABCD.EFGH."
+            scope="PRJ" qualifier="BRC" long_name="Struts Data"
+            description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" authorization_updated_at="[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"
+            uuid="GHIJ" project_uuid="ABCD" module_uuid="FGHI" module_uuid_path="ABCD.EFGH.FGHI."
+            name="src/org/struts" root_id="3"
+            description="[null]"
+            enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/org/struts" authorization_updated_at="[null]" />
+  <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"
+            uuid="HIJK" project_uuid="ABCD" module_uuid="GHIJ" module_uuid_path="ABCD.EFGH.FGHI.GHIJ."
+            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" authorization_updated_at="[null]" />
+
+  <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>