aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-core
diff options
context:
space:
mode:
authorFabrice Bellingard <bellingard@gmail.com>2012-06-29 11:21:53 +0200
committerFabrice Bellingard <bellingard@gmail.com>2012-06-29 11:23:56 +0200
commit94b831e62c22a1465adbde298bdc2d1d804a73fc (patch)
tree7fc8efa5fa0ea206390ad5f01f679a4a61998d95 /sonar-core
parentcbf9a517c208b45b1b6b719436fa08ef217e5196 (diff)
downloadsonarqube-94b831e62c22a1465adbde298bdc2d1d804a73fc.tar.gz
sonarqube-94b831e62c22a1465adbde298bdc2d1d804a73fc.zip
SONAR-1608 Make it possible to modify the key of projects
Diffstat (limited to 'sonar-core')
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java2
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java2
-rw-r--r--sonar-core/src/main/java/org/sonar/core/resource/ResourceDto.java26
-rw-r--r--sonar-core/src/main/java/org/sonar/core/resource/ResourceKeyUpdaterDao.java119
-rw-r--r--sonar-core/src/main/java/org/sonar/core/resource/ResourceKeyUpdaterMapper.java39
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/resource/ResourceKeyUpdaterMapper.xml38
-rw-r--r--sonar-core/src/test/java/org/sonar/core/resource/ResourceKeyUpdaterDaoTest.java87
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shared.xml50
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldBulkUpdateKey-result.xml50
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldNotUpdateAllSubmodules-result.xml44
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldNotUpdateAllSubmodules.xml44
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldUpdateKey-result.xml52
12 files changed, 553 insertions, 0 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
index 8014061c509..602181cdd44 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
@@ -28,6 +28,7 @@ import org.sonar.core.properties.PropertiesDao;
import org.sonar.core.purge.PurgeDao;
import org.sonar.core.resource.ResourceDao;
import org.sonar.core.resource.ResourceIndexerDao;
+import org.sonar.core.resource.ResourceKeyUpdaterDao;
import org.sonar.core.review.ReviewCommentDao;
import org.sonar.core.review.ReviewDao;
import org.sonar.core.rule.RuleDao;
@@ -54,6 +55,7 @@ public final class DaoUtils {
PurgeDao.class,
ResourceIndexerDao.class,
ResourceDao.class,
+ ResourceKeyUpdaterDao.class,
ReviewCommentDao.class,
ReviewDao.class,
RuleDao.class);
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
index 6f1d450e016..dd7d58cb20a 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
@@ -61,6 +61,7 @@ import org.sonar.core.purge.PurgeableSnapshotDto;
import org.sonar.core.resource.ResourceDto;
import org.sonar.core.resource.ResourceIndexDto;
import org.sonar.core.resource.ResourceIndexerMapper;
+import org.sonar.core.resource.ResourceKeyUpdaterMapper;
import org.sonar.core.resource.ResourceMapper;
import org.sonar.core.resource.SnapshotDto;
import org.sonar.core.review.ReviewCommentDto;
@@ -130,6 +131,7 @@ public class MyBatis implements BatchComponent, ServerComponent {
loadMapper(conf, PurgeMapper.class);
loadMapper(conf, PurgeVendorMapper.class);
loadMapper(conf, ResourceMapper.class);
+ loadMapper(conf, ResourceKeyUpdaterMapper.class);
loadMapper(conf, ReviewCommentMapper.class);
loadMapper(conf, ReviewMapper.class);
loadMapper(conf, ResourceIndexerMapper.class);
diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDto.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDto.java
index 212b954fc88..193984ccbff 100644
--- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDto.java
+++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDto.java
@@ -91,4 +91,30 @@ public final class ResourceDto {
this.qualifier = qualifier;
return this;
}
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ResourceDto other = (ResourceDto) obj;
+ if (id == null) {
+ if (other.id != null)
+ return false;
+ } else if (!id.equals(other.id))
+ return false;
+ return true;
+ }
+
}
diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceKeyUpdaterDao.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceKeyUpdaterDao.java
new file mode 100644
index 00000000000..d6d8dbf1a70
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceKeyUpdaterDao.java
@@ -0,0 +1,119 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.core.resource;
+
+import com.google.common.collect.Sets;
+import org.apache.ibatis.session.SqlSession;
+import org.sonar.core.persistence.MyBatis;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Class used to rename the key of a project and its resources.
+ *
+ * @since 3.2
+ */
+public class ResourceKeyUpdaterDao {
+ private MyBatis mybatis;
+
+ public ResourceKeyUpdaterDao(MyBatis mybatis) {
+ this.mybatis = mybatis;
+ }
+
+ public void updateKey(long projectId, String newKey) {
+ SqlSession session = mybatis.openBatchSession();
+ ResourceKeyUpdaterMapper mapper = session.getMapper(ResourceKeyUpdaterMapper.class);
+ try {
+ if (mapper.countResourceByKey(newKey) > 0) {
+ throw new IllegalStateException("Impossible to update key: a resource with \"" + newKey + "\" key already exists.");
+ }
+
+ // must SELECT first everything
+ ResourceDto project = mapper.selectProject(projectId);
+ String projectOldKey = project.getKey();
+ List<ResourceDto> resources = mapper.selectProjectResources(projectId);
+ resources.add(project);
+
+ // and then proceed with the batch UPDATE at once
+ runBatchUpdateForAllResources(resources, projectOldKey, newKey, mapper);
+
+ session.commit();
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ public void bulkUpdateKey(long projectId, String oldPrefix, String newPrefix) {
+ SqlSession session = mybatis.openBatchSession();
+ ResourceKeyUpdaterMapper mapper = session.getMapper(ResourceKeyUpdaterMapper.class);
+ try {
+ // must SELECT first everything
+ Set<ResourceDto> modules = collectAllModules(projectId, oldPrefix, mapper);
+ checkNewNameOfAllModules(modules, oldPrefix, newPrefix, mapper);
+ Set<ResourceDto> allResources = Sets.newHashSet(modules);
+ for (ResourceDto module : modules) {
+ allResources.addAll(mapper.selectProjectResources(module.getId()));
+ }
+
+ // and then proceed with the batch UPDATE at once
+ runBatchUpdateForAllResources(allResources, oldPrefix, newPrefix, mapper);
+
+ session.commit();
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ private String computeNewKey(ResourceDto resource, String oldPrefix, String newPrefix) {
+ String resourceKey = resource.getKey();
+ return newPrefix + resourceKey.substring(oldPrefix.length(), resourceKey.length());
+ }
+
+ private void runBatchUpdateForAllResources(Collection<ResourceDto> resources, String oldPrefix, String newPrefix, ResourceKeyUpdaterMapper mapper) {
+ for (ResourceDto resource : resources) {
+ resource.setKey(computeNewKey(resource, oldPrefix, newPrefix));
+ mapper.update(resource);
+ }
+ }
+
+ private Set<ResourceDto> collectAllModules(long projectId, String oldPrefix, ResourceKeyUpdaterMapper mapper) {
+ ResourceDto project = mapper.selectProject(projectId);
+ Set<ResourceDto> modules = Sets.newHashSet();
+ if (project.getKey().startsWith(oldPrefix)) {
+ modules.add(project);
+ }
+ for (ResourceDto submodule : mapper.selectDescendantProjects(projectId)) {
+ modules.addAll(collectAllModules(submodule.getId(), oldPrefix, mapper));
+ }
+ return modules;
+ }
+
+ private void checkNewNameOfAllModules(Set<ResourceDto> modules, String oldPrefix, String newPrefix, ResourceKeyUpdaterMapper mapper) {
+ for (ResourceDto module : modules) {
+ String newName = computeNewKey(module, oldPrefix, newPrefix);
+ if (mapper.countResourceByKey(newName) > 0) {
+ throw new IllegalStateException("Impossible to update key: a resource with \"" + newName + "\" key already exists.");
+ }
+ }
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceKeyUpdaterMapper.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceKeyUpdaterMapper.java
new file mode 100644
index 00000000000..a6ccff97ed2
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceKeyUpdaterMapper.java
@@ -0,0 +1,39 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.core.resource;
+
+import java.util.List;
+
+/**
+ * @since 3.2
+ */
+public interface ResourceKeyUpdaterMapper {
+
+ int countResourceByKey(String key);
+
+ ResourceDto selectProject(long projectId);
+
+ List<ResourceDto> selectProjectResources(long projectId);
+
+ List<ResourceDto> selectDescendantProjects(long projectId);
+
+ void update(ResourceDto resource);
+
+}
diff --git a/sonar-core/src/main/resources/org/sonar/core/resource/ResourceKeyUpdaterMapper.xml b/sonar-core/src/main/resources/org/sonar/core/resource/ResourceKeyUpdaterMapper.xml
new file mode 100644
index 00000000000..2fc0a3f4231
--- /dev/null
+++ b/sonar-core/src/main/resources/org/sonar/core/resource/ResourceKeyUpdaterMapper.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.core.resource.ResourceKeyUpdaterMapper">
+
+ <resultMap id="resourceResultMap" type="Resource">
+ <id property="id" column="id"/>
+ <result property="key" column="kee"/>
+ <result property="rootId" column="root_id"/>
+ <result property="scope" column="scope"/>
+ </resultMap>
+
+ <select id="countResourceByKey" parameterType="String" resultType="int">
+ SELECT count(*)
+ FROM projects
+ WHERE kee = #{key}
+ </select>
+
+ <select id="selectProject" parameterType="long" resultMap="resourceResultMap">
+ select * from projects where id=#{id}
+ </select>
+
+ <select id="selectProjectResources" parameterType="long" resultMap="resourceResultMap">
+ select * from projects where root_id=#{id} AND scope!='PRJ'
+ </select>
+
+ <select id="selectDescendantProjects" parameterType="long" resultMap="resourceResultMap">
+ select * from projects where scope='PRJ' and root_id=#{id}
+ </select>
+
+ <update id="update" parameterType="Resource">
+ update projects
+ set kee = #{key, jdbcType=VARCHAR}
+ where id = #{id}
+ </update>
+
+</mapper>
+
diff --git a/sonar-core/src/test/java/org/sonar/core/resource/ResourceKeyUpdaterDaoTest.java b/sonar-core/src/test/java/org/sonar/core/resource/ResourceKeyUpdaterDaoTest.java
new file mode 100644
index 00000000000..bfa64d86dc4
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/resource/ResourceKeyUpdaterDaoTest.java
@@ -0,0 +1,87 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.core.resource;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.core.persistence.DaoTestCase;
+
+public class ResourceKeyUpdaterDaoTest extends DaoTestCase {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private ResourceKeyUpdaterDao dao;
+
+ @Before
+ public void createDao() {
+ dao = new ResourceKeyUpdaterDao(getMyBatis());
+ }
+
+ @Test
+ public void shouldUpdateKey() {
+ setupData("shared");
+
+ dao.updateKey(2, "struts:core");
+
+ checkTables("shouldUpdateKey", "projects");
+ }
+
+ @Test
+ public void shouldNotUpdateKey() {
+ setupData("shared");
+
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Impossible to update key: a resource with \"org.struts:struts-ui\" key already exists.");
+
+ dao.updateKey(2, "org.struts:struts-ui");
+ }
+
+ @Test
+ public void shouldBulkUpdateKey() {
+ setupData("shared");
+
+ dao.bulkUpdateKey(1, "org.struts", "org.apache.struts");
+
+ checkTables("shouldBulkUpdateKey", "projects");
+ }
+
+ @Test
+ public void shouldFailBulkUpdateKeyIfKeyAlreadyExist() {
+ setupData("shared");
+
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Impossible to update key: a resource with \"foo:struts-core\" key already exists.");
+
+ dao.bulkUpdateKey(1, "org.struts", "foo");
+ }
+
+ @Test
+ public void shouldNotUpdateAllSubmodules() throws Exception {
+ setupData("shouldNotUpdateAllSubmodules");
+
+ dao.bulkUpdateKey(1, "org.struts", "org.apache.struts");
+
+ checkTables("shouldNotUpdateAllSubmodules", "projects");
+ }
+
+}
diff --git a/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shared.xml b/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shared.xml
new file mode 100644
index 00000000000..e81e23c7b4d
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shared.xml
@@ -0,0 +1,50 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ description="[null]" long_name="Apache Struts" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+ <!-- **************** First sub project **************** -->
+ <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core" profile_id="1"
+ description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- directory -->
+ <projects long_name="org.struts" id="3" scope="DIR" qualifier="PAC" kee="org.struts:struts-core:org.struts"
+ name="org.struts" root_id="2"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="org.struts.RequestContext" id="4" scope="FIL" qualifier="CLA" kee="org.struts:struts-core:org.struts.RequestContext"
+ name="RequestContext" root_id="2"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+ <!-- **************** Second sub project **************** -->
+ <projects id="5" root_id="1" kee="org.struts:struts-ui" name="Struts UI"
+ scope="PRJ" qualifier="BRC" long_name="Struts UI" profile_id="1"
+ description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- directory -->
+ <projects long_name="org.struts" id="6" scope="DIR" qualifier="PAC" kee="org.struts:struts-ui:org.struts"
+ name="org.struts" root_id="5"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="org.struts.RequestContext" id="7" scope="FIL" qualifier="CLA" kee="org.struts:struts-ui:org.struts.RequestContext"
+ name="RequestContext" root_id="5"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+ <!-- **************** Another independent project **************** -->
+ <projects id="8" root_id="[null]" kee="foo:struts-core" name="Foo Struts Core"
+ scope="PRJ" qualifier="BRC" long_name="Foo Struts Core" profile_id="1"
+ description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldBulkUpdateKey-result.xml b/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldBulkUpdateKey-result.xml
new file mode 100644
index 00000000000..8c2ef069840
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldBulkUpdateKey-result.xml
@@ -0,0 +1,50 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.apache.struts:struts" name="Struts"
+ description="[null]" long_name="Apache Struts" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+ <!-- **************** First sub project **************** -->
+ <projects id="2" root_id="1" kee="org.apache.struts:struts-core" name="Struts Core"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core" profile_id="1"
+ description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- directory -->
+ <projects long_name="org.struts" id="3" scope="DIR" qualifier="PAC" kee="org.apache.struts:struts-core:org.struts"
+ name="org.struts" root_id="2"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="org.struts.RequestContext" id="4" scope="FIL" qualifier="CLA" kee="org.apache.struts:struts-core:org.struts.RequestContext"
+ name="RequestContext" root_id="2"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+ <!-- **************** Second sub project **************** -->
+ <projects id="5" root_id="1" kee="org.apache.struts:struts-ui" name="Struts UI"
+ scope="PRJ" qualifier="BRC" long_name="Struts UI" profile_id="1"
+ description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- directory -->
+ <projects long_name="org.struts" id="6" scope="DIR" qualifier="PAC" kee="org.apache.struts:struts-ui:org.struts"
+ name="org.struts" root_id="5"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="org.struts.RequestContext" id="7" scope="FIL" qualifier="CLA" kee="org.apache.struts:struts-ui:org.struts.RequestContext"
+ name="RequestContext" root_id="5"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+ <!-- **************** Another independent project **************** -->
+ <projects id="8" root_id="[null]" kee="foo:struts-core" name="Foo Struts Core"
+ scope="PRJ" qualifier="BRC" long_name="Foo Struts Core" profile_id="1"
+ description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldNotUpdateAllSubmodules-result.xml b/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldNotUpdateAllSubmodules-result.xml
new file mode 100644
index 00000000000..b8f9d1d9d3b
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldNotUpdateAllSubmodules-result.xml
@@ -0,0 +1,44 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.apache.struts:struts" name="Struts"
+ description="[null]" long_name="Apache Struts" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+ <!-- **************** First sub project **************** -->
+ <projects id="2" root_id="1" kee="org.apache.struts:struts-core" name="Struts Core"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core" profile_id="1"
+ description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- directory -->
+ <projects long_name="org.struts" id="3" scope="DIR" qualifier="PAC" kee="org.apache.struts:struts-core:org.struts"
+ name="org.struts" root_id="2"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="org.struts.RequestContext" id="4" scope="FIL" qualifier="CLA" kee="org.apache.struts:struts-core:org.struts.RequestContext"
+ name="RequestContext" root_id="2"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+ <!-- **************** Second sub project THAT HAS A DIFFERENT GROUP ID => MUST NOT BE UPDATED **************** -->
+ <projects id="5" root_id="1" kee="foo:struts-ui" name="Struts UI"
+ scope="PRJ" qualifier="BRC" long_name="Struts UI" profile_id="1"
+ description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- directory -->
+ <projects long_name="org.struts" id="6" scope="DIR" qualifier="PAC" kee="foo:struts-ui:org.struts"
+ name="org.struts" root_id="5"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="org.struts.RequestContext" id="7" scope="FIL" qualifier="CLA" kee="foo:struts-ui:org.struts.RequestContext"
+ name="RequestContext" root_id="5"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldNotUpdateAllSubmodules.xml b/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldNotUpdateAllSubmodules.xml
new file mode 100644
index 00000000000..1b33265baeb
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldNotUpdateAllSubmodules.xml
@@ -0,0 +1,44 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ description="[null]" long_name="Apache Struts" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+ <!-- **************** First sub project **************** -->
+ <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core" profile_id="1"
+ description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- directory -->
+ <projects long_name="org.struts" id="3" scope="DIR" qualifier="PAC" kee="org.struts:struts-core:org.struts"
+ name="org.struts" root_id="2"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="org.struts.RequestContext" id="4" scope="FIL" qualifier="CLA" kee="org.struts:struts-core:org.struts.RequestContext"
+ name="RequestContext" root_id="2"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+ <!-- **************** Second sub project THAT HAS A DIFFERENT GROUP ID => MUST NOT BE UPDATED **************** -->
+ <projects id="5" root_id="1" kee="foo:struts-ui" name="Struts UI"
+ scope="PRJ" qualifier="BRC" long_name="Struts UI" profile_id="1"
+ description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- directory -->
+ <projects long_name="org.struts" id="6" scope="DIR" qualifier="PAC" kee="foo:struts-ui:org.struts"
+ name="org.struts" root_id="5"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="org.struts.RequestContext" id="7" scope="FIL" qualifier="CLA" kee="foo:struts-ui:org.struts.RequestContext"
+ name="RequestContext" root_id="5"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldUpdateKey-result.xml b/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldUpdateKey-result.xml
new file mode 100644
index 00000000000..229d8f44941
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldUpdateKey-result.xml
@@ -0,0 +1,52 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ description="[null]" long_name="Apache Struts" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+ <!-- **************** First sub project **************** -->
+ <!-- ONLY THIS PROJECT MUST HAVE BEEN UPDATED -->
+ <!-- -->
+ <projects id="2" root_id="1" kee="struts:core" name="Struts Core"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core" profile_id="1"
+ description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- directory -->
+ <projects long_name="org.struts" id="3" scope="DIR" qualifier="PAC" kee="struts:core:org.struts"
+ name="org.struts" root_id="2"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="org.struts.RequestContext" id="4" scope="FIL" qualifier="CLA" kee="struts:core:org.struts.RequestContext"
+ name="RequestContext" root_id="2"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+ <!-- **************** Second sub project **************** -->
+ <projects id="5" root_id="1" kee="org.struts:struts-ui" name="Struts UI"
+ scope="PRJ" qualifier="BRC" long_name="Struts UI" profile_id="1"
+ description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- directory -->
+ <projects long_name="org.struts" id="6" scope="DIR" qualifier="PAC" kee="org.struts:struts-ui:org.struts"
+ name="org.struts" root_id="5"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="org.struts.RequestContext" id="7" scope="FIL" qualifier="CLA" kee="org.struts:struts-ui:org.struts.RequestContext"
+ name="RequestContext" root_id="5"
+ description="[null]" profile_id="1"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+ <!-- **************** Another independent project **************** -->
+ <projects id="8" root_id="[null]" kee="foo:struts-core" name="Foo Struts Core"
+ scope="PRJ" qualifier="BRC" long_name="Foo Struts Core" profile_id="1"
+ description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+</dataset>