diff options
31 files changed, 470 insertions, 174 deletions
diff --git a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java index a02d8d0a308..f449b5c38e0 100644 --- a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java +++ b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java @@ -19,54 +19,45 @@ */ package org.sonar.plugins.dbcleaner; -import java.util.Arrays; -import java.util.List; - import org.sonar.api.Properties; import org.sonar.api.Property; import org.sonar.api.SonarPlugin; +import org.sonar.plugins.dbcleaner.api.DbCleanerCommands; import org.sonar.plugins.dbcleaner.api.DbCleanerConstants; import org.sonar.plugins.dbcleaner.period.DefaultPeriodCleaner; import org.sonar.plugins.dbcleaner.period.PeriodPurge; -import org.sonar.plugins.dbcleaner.purges.PurgeDeletedResources; -import org.sonar.plugins.dbcleaner.purges.PurgeDependencies; -import org.sonar.plugins.dbcleaner.purges.PurgeDeprecatedLast; -import org.sonar.plugins.dbcleaner.purges.PurgeDisabledResources; -import org.sonar.plugins.dbcleaner.purges.PurgeEntities; -import org.sonar.plugins.dbcleaner.purges.PurgeEventOrphans; -import org.sonar.plugins.dbcleaner.purges.PurgeOrphanResources; -import org.sonar.plugins.dbcleaner.purges.PurgeOrphanReviews; -import org.sonar.plugins.dbcleaner.purges.PurgePropertyOrphans; -import org.sonar.plugins.dbcleaner.purges.PurgeResourceRoles; -import org.sonar.plugins.dbcleaner.purges.PurgeRuleMeasures; -import org.sonar.plugins.dbcleaner.purges.PurgeUnprocessed; +import org.sonar.plugins.dbcleaner.purges.*; import org.sonar.plugins.dbcleaner.runner.PurgeRunner; +import java.util.Arrays; +import java.util.List; + @Properties({ - @Property(key = DbCleanerConstants.MONTHS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_WEEK, defaultValue = DbCleanerConstants.ONE_MONTH, - name = "Number of months before starting to keep only one snapshot by week", - description = "After this number of months, if there are several snapshots during the same week, " - + "the DbCleaner keeps the first one and fully delete the other ones.", global = true, project = true), - @Property(key = DbCleanerConstants.MONTHS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_MONTH, defaultValue = DbCleanerConstants.ONE_YEAR, - name = "Number of months before starting to keep only one snapshot by month", - description = "After this number of months, if there are several snapshots during the same month, " - + "the DbCleaner keeps the first one and fully delete the other ones.", global = true, project = true), - @Property(key = DbCleanerConstants.MONTHS_BEFORE_DELETING_ALL_SNAPSHOTS, defaultValue = DbCleanerConstants.FIVE_YEARS, - name = "Number of months before starting to delete all remaining snapshots", - description = "After this number of months, all snapshots are fully deleted.", global = true, project = true) }) + @Property(key = DbCleanerConstants.MONTHS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_WEEK, defaultValue = DbCleanerConstants.ONE_MONTH, + name = "Number of months before starting to keep only one snapshot by week", + description = "After this number of months, if there are several snapshots during the same week, " + + "the DbCleaner keeps the first one and fully delete the other ones.", global = true, project = true), + @Property(key = DbCleanerConstants.MONTHS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_MONTH, defaultValue = DbCleanerConstants.ONE_YEAR, + name = "Number of months before starting to keep only one snapshot by month", + description = "After this number of months, if there are several snapshots during the same month, " + + "the DbCleaner keeps the first one and fully delete the other ones.", global = true, project = true), + @Property(key = DbCleanerConstants.MONTHS_BEFORE_DELETING_ALL_SNAPSHOTS, defaultValue = DbCleanerConstants.FIVE_YEARS, + name = "Number of months before starting to delete all remaining snapshots", + description = "After this number of months, all snapshots are fully deleted.", global = true, project = true)}) public final class DbCleanerPlugin extends SonarPlugin { public List getExtensions() { return Arrays.asList( - // shared components - DefaultPeriodCleaner.class, + // shared components + DefaultPeriodCleaner.class, + DbCleanerCommands.class, - // purges - PurgeOrphanResources.class, PurgeEntities.class, PurgeRuleMeasures.class, PurgeUnprocessed.class, PurgeDeletedResources.class, - PurgeDeprecatedLast.class, PurgeDisabledResources.class, PurgeResourceRoles.class, PurgeEventOrphans.class, - PurgePropertyOrphans.class, PeriodPurge.class, PurgeDependencies.class, PurgeOrphanReviews.class, + // purges + PurgeOrphanResources.class, PurgeEntities.class, PurgeRuleMeasures.class, PurgeUnprocessed.class, PurgeDeletedResources.class, + PurgeDeprecatedLast.class, PurgeDisabledResources.class, PurgeResourceRoles.class, PurgeEventOrphans.class, + PurgePropertyOrphans.class, PeriodPurge.class, PurgeDependencies.class, PurgeOrphanReviews.class, - // post-job - PurgeRunner.class); + // post-job + PurgeRunner.class); } } diff --git a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/api/DbCleanerCommands.java b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/api/DbCleanerCommands.java new file mode 100644 index 00000000000..b010988a0cd --- /dev/null +++ b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/api/DbCleanerCommands.java @@ -0,0 +1,56 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 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.plugins.dbcleaner.api; + +import org.sonar.api.BatchExtension; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.model.ResourceModel; +import org.sonar.persistence.resource.ResourceIndexerDao; + +import java.util.List; + +/** + * @since 2.13 + */ +public class DbCleanerCommands implements BatchExtension { + + private DatabaseSession session; + private ResourceIndexerDao resourceIndexer; + + public DbCleanerCommands(DatabaseSession session, ResourceIndexerDao resourceIndexer) { + this.session = session; + this.resourceIndexer = resourceIndexer; + } + + public DbCleanerCommands deleteSnapshots(List<Integer> snapshotIds, boolean includeDependents) { + if (includeDependents) { + PurgeUtils.deleteSnapshotsData(session, snapshotIds); + } else { + PurgeUtils.deleteSnapshots(session, snapshotIds); + } + return this; + } + + public DbCleanerCommands deleteResources(List<Integer> resourceIds) { + PurgeUtils.executeQuery(session, "", resourceIds, "DELETE FROM " + ResourceModel.class.getSimpleName() + " WHERE id in (:ids)"); + resourceIndexer.delete(resourceIds); + return this; + } +} diff --git a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeDisabledResources.java b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeDisabledResources.java index 267876d0928..dbf22a5004d 100644 --- a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeDisabledResources.java +++ b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeDisabledResources.java @@ -22,34 +22,28 @@ package org.sonar.plugins.dbcleaner.purges; import org.sonar.api.database.DatabaseSession; import org.sonar.api.database.model.ResourceModel; import org.sonar.api.database.model.Snapshot; - +import org.sonar.plugins.dbcleaner.api.DbCleanerCommands; import org.sonar.plugins.dbcleaner.api.Purge; import org.sonar.plugins.dbcleaner.api.PurgeContext; -import org.sonar.plugins.dbcleaner.api.PurgeUtils; - -import java.util.List; import javax.persistence.Query; +import java.util.List; /** * @since 1.11 */ public final class PurgeDisabledResources extends Purge { - public PurgeDisabledResources(DatabaseSession session) { + private DbCleanerCommands dbCleanerCommands; + + public PurgeDisabledResources(DatabaseSession session, DbCleanerCommands dbCleanerCommands) { super(session); + this.dbCleanerCommands = dbCleanerCommands; } public void purge(PurgeContext context) { - PurgeUtils.deleteSnapshotsData(getSession(), getSnapshotIds()); - deleteResources(); - } - - private void deleteResources() { - final List<Integer> resourceIds = getResourceIds(); - if ( !resourceIds.isEmpty()) { - PurgeUtils.executeQuery(getSession(), "", resourceIds, "delete from " + ResourceModel.class.getSimpleName() + " r where r.id in (:ids)"); - } + dbCleanerCommands.deleteSnapshots(getSnapshotIds(), true); + dbCleanerCommands.deleteResources(getResourceIds()); } private List<Integer> getResourceIds() { @@ -60,7 +54,7 @@ public final class PurgeDisabledResources extends Purge { private List<Integer> getSnapshotIds() { Query query = getSession().createQuery("SELECT s.id FROM " + Snapshot.class.getSimpleName() + " s WHERE " + - " EXISTS (FROM " + ResourceModel.class.getSimpleName() + " r WHERE r.id=s.resourceId AND r.enabled=:enabled)"); + " EXISTS (FROM " + ResourceModel.class.getSimpleName() + " r WHERE r.id=s.resourceId AND r.enabled=:enabled)"); query.setParameter("enabled", Boolean.FALSE); return query.getResultList(); } diff --git a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanResources.java b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanResources.java index 9178b5ad85d..a38ead16fbb 100644 --- a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanResources.java +++ b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanResources.java @@ -21,29 +21,31 @@ package org.sonar.plugins.dbcleaner.purges; import org.sonar.api.database.DatabaseSession; import org.sonar.api.database.model.ResourceModel; +import org.sonar.plugins.dbcleaner.api.DbCleanerCommands; import org.sonar.plugins.dbcleaner.api.Purge; import org.sonar.plugins.dbcleaner.api.PurgeContext; -import org.sonar.plugins.dbcleaner.api.PurgeUtils; - -import java.util.List; import javax.persistence.Query; +import java.util.List; /** * @since 2.1 */ public final class PurgeOrphanResources extends Purge { - public PurgeOrphanResources(DatabaseSession session) { + private DbCleanerCommands dbCleanerCommands; + + public PurgeOrphanResources(DatabaseSession session, DbCleanerCommands dbCleanerCommands) { super(session); + this.dbCleanerCommands = dbCleanerCommands; } public void purge(PurgeContext context) { Query query = getSession().createQuery("SELECT r1.id FROM " + ResourceModel.class.getSimpleName() + - " r1 WHERE r1.rootId IS NOT NULL AND NOT EXISTS(FROM " + ResourceModel.class.getSimpleName() + " r2 WHERE r1.rootId=r2.id)"); + " r1 WHERE r1.rootId IS NOT NULL AND NOT EXISTS(FROM " + ResourceModel.class.getSimpleName() + " r2 WHERE r1.rootId=r2.id)"); List<Integer> idsToDelete = query.getResultList(); if (!idsToDelete.isEmpty()) { - PurgeUtils.executeQuery(getSession(), "", idsToDelete, "DELETE FROM " + ResourceModel.class.getSimpleName() + " WHERE id in (:ids)"); + dbCleanerCommands.deleteResources(idsToDelete); } } } diff --git a/plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeDisabledResourcesTest.java b/plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeDisabledResourcesTest.java index 8ff3d3d84c3..2e87714e2e8 100644 --- a/plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeDisabledResourcesTest.java +++ b/plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeDisabledResourcesTest.java @@ -21,9 +21,13 @@ package org.sonar.plugins.dbcleaner.purges; import org.junit.Test; import org.sonar.jpa.test.AbstractDbUnitTestCase; +import org.sonar.persistence.resource.ResourceIndexerDao; +import org.sonar.plugins.dbcleaner.api.DbCleanerCommands; import java.sql.SQLException; +import static org.mockito.Mockito.mock; + public class PurgeDisabledResourcesTest extends AbstractDbUnitTestCase { @Test @@ -43,7 +47,7 @@ public class PurgeDisabledResourcesTest extends AbstractDbUnitTestCase { private void assertPurge(String testName) { setupData("sharedFixture", testName); - new PurgeDisabledResources(getSession()).purge(null); + new PurgeDisabledResources(getSession(), new DbCleanerCommands(getSession(), mock(ResourceIndexerDao.class))).purge(null); checkTables(testName, "snapshots", "project_measures"); } } diff --git a/plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanResourcesTest.java b/plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanResourcesTest.java index 87ba7fb9c8b..5f443998ce3 100644 --- a/plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanResourcesTest.java +++ b/plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanResourcesTest.java @@ -21,9 +21,13 @@ package org.sonar.plugins.dbcleaner.purges; import org.junit.Test; import org.sonar.jpa.test.AbstractDbUnitTestCase; +import org.sonar.persistence.resource.ResourceIndexerDao; +import org.sonar.plugins.dbcleaner.api.DbCleanerCommands; import java.sql.SQLException; +import static org.mockito.Mockito.mock; + public class PurgeOrphanResourcesTest extends AbstractDbUnitTestCase { @Test public void purgeOrphanResources() throws SQLException { @@ -32,7 +36,7 @@ public class PurgeOrphanResourcesTest extends AbstractDbUnitTestCase { private void assertPurge(String testName) { setupData(testName); - new PurgeOrphanResources(getSession()).purge(null); + new PurgeOrphanResources(getSession(), new DbCleanerCommands(getSession(), mock(ResourceIndexerDao.class))).purge(null); checkTables(testName, "projects"); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java index dd8bf85d196..127d3415208 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java @@ -27,14 +27,10 @@ import org.sonar.api.database.model.ResourceModel; import org.sonar.api.database.model.Snapshot; import org.sonar.api.resources.*; import org.sonar.api.utils.SonarException; +import org.sonar.persistence.resource.ResourceIndexerDao; import javax.persistence.NonUniqueResultException; import javax.persistence.Query; - -import org.sonar.api.resources.Qualifiers; - -import org.sonar.api.resources.Scopes; - import java.util.Iterator; import java.util.List; import java.util.Map; @@ -44,9 +40,11 @@ public final class DefaultResourcePersister implements ResourcePersister { private DatabaseSession session; private Map<Resource, Snapshot> snapshotsByResource = Maps.newHashMap(); + private ResourceIndexerDao indexer; - public DefaultResourcePersister(DatabaseSession session) { + public DefaultResourcePersister(DatabaseSession session, ResourceIndexerDao indexer) { this.session = session; + this.indexer = indexer; } public Snapshot saveProject(Project project, Project parent) { @@ -78,13 +76,14 @@ public final class DefaultResourcePersister implements ResourcePersister { model.setRootId((Integer) ObjectUtils.defaultIfNull(parentSnapshot.getRootProjectId(), parentSnapshot.getResourceId())); } model = session.save(model); - project.setId(model.getId()); // TODO to be removed + project.setId(model.getId()); Snapshot snapshot = new Snapshot(model, parentSnapshot); snapshot.setVersion(project.getAnalysisVersion()); snapshot.setCreatedAt(project.getAnalysisDate()); snapshot = session.save(snapshot); session.commit(); + indexer.index(project.getName(), snapshot.getResourceId(), snapshot.getRootProjectId()); return snapshot; } @@ -134,6 +133,7 @@ public final class DefaultResourcePersister implements ResourcePersister { } else { snapshot = persistFileOrDirectory(project, resource, parent); } + indexer.index(resource.getName(), snapshot.getResourceId(), snapshot.getRootProjectId()); return snapshot; } @@ -162,7 +162,7 @@ public final class DefaultResourcePersister implements ResourcePersister { private Snapshot findLibrarySnapshot(Integer resourceId, String version) { Query query = session.createQuery("from " + Snapshot.class.getSimpleName() + - " s WHERE s.resourceId=:resourceId AND s.version=:version AND s.scope=:scope AND s.qualifier<>:qualifier AND s.last=:last"); + " s WHERE s.resourceId=:resourceId AND s.version=:version AND s.scope=:scope AND s.qualifier<>:qualifier AND s.last=:last"); query.setParameter("resourceId", resourceId); query.setParameter("version", version); query.setParameter("scope", Scopes.PROJECT); @@ -183,7 +183,7 @@ public final class DefaultResourcePersister implements ResourcePersister { Snapshot projectSnapshot = snapshotsByResource.get(project); model.setRootId(projectSnapshot.getResourceId()); model = session.save(model); - resource.setId(model.getId()); // TODO to be removed + resource.setId(model.getId()); Snapshot parentSnapshot = (Snapshot) ObjectUtils.defaultIfNull(getSnapshot(parentReference), projectSnapshot); Snapshot snapshot = new Snapshot(model, parentSnapshot); @@ -208,7 +208,7 @@ public final class DefaultResourcePersister implements ResourcePersister { public void clear() { // we keep cache of projects - for (Iterator<Map.Entry<Resource, Snapshot>> it = snapshotsByResource.entrySet().iterator(); it.hasNext();) { + for (Iterator<Map.Entry<Resource, Snapshot>> it = snapshotsByResource.entrySet().iterator(); it.hasNext(); ) { Map.Entry<Resource, Snapshot> entry = it.next(); if (!ResourceUtils.isSet(entry.getKey())) { it.remove(); diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultResourcePersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultResourcePersisterTest.java index 3491738c922..8f5c8a14e5c 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultResourcePersisterTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultResourcePersisterTest.java @@ -26,6 +26,7 @@ import org.sonar.api.resources.JavaPackage; import org.sonar.api.resources.Library; import org.sonar.api.resources.Project; import org.sonar.jpa.test.AbstractDbUnitTestCase; +import org.sonar.persistence.resource.ResourceIndexerDao; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -33,6 +34,7 @@ import java.text.SimpleDateFormat; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.Mockito.mock; public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { @@ -65,7 +67,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { public void shouldSaveNewProject() { setupData("shared"); - ResourcePersister persister = new DefaultResourcePersister(getSession()); + ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourceIndexerDao.class)); persister.saveProject(singleProject, null); checkTables("shouldSaveNewProject", "projects", "snapshots"); @@ -75,7 +77,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { public void shouldSaveNewMultiModulesProject() throws ParseException { setupData("shared"); - ResourcePersister persister = new DefaultResourcePersister(getSession()); + ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourceIndexerDao.class)); persister.saveProject(multiModuleProject, null); persister.saveProject(moduleA, multiModuleProject); persister.saveProject(moduleB, multiModuleProject); @@ -88,7 +90,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { public void shouldSaveNewDirectory() { setupData("shared"); - ResourcePersister persister = new DefaultResourcePersister(getSession()); + ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourceIndexerDao.class)); persister.saveProject(singleProject, null); persister.saveResource(singleProject, new JavaPackage("org.foo").setEffectiveKey("foo:org.foo")); @@ -100,7 +102,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { public void shouldSaveNewLibrary() { setupData("shared"); - ResourcePersister persister = new DefaultResourcePersister(getSession()); + ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourceIndexerDao.class)); persister.saveProject(singleProject, null); persister.saveResource(singleProject, new Library("junit:junit", "4.8.2").setEffectiveKey("junit:junit")); persister.saveResource(singleProject, new Library("junit:junit", "4.8.2").setEffectiveKey("junit:junit"));// do nothing, already saved @@ -113,7 +115,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { public void shouldClearResourcesExceptProjects() { setupData("shared"); - DefaultResourcePersister persister = new DefaultResourcePersister(getSession()); + DefaultResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourceIndexerDao.class)); persister.saveProject(multiModuleProject, null); persister.saveProject(moduleA, multiModuleProject); persister.saveResource(moduleA, new JavaPackage("org.foo").setEffectiveKey("a:org.foo")); @@ -129,7 +131,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { public void shouldUpdateExistingResource() { setupData("shouldUpdateExistingResource"); - ResourcePersister persister = new DefaultResourcePersister(getSession()); + ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourceIndexerDao.class)); singleProject.setName("new name"); singleProject.setDescription("new description"); persister.saveProject(singleProject, null); diff --git a/sonar-batch/src/test/java/org/sonar/batch/phases/UpdateStatusJobTest.java b/sonar-batch/src/test/java/org/sonar/batch/phases/UpdateStatusJobTest.java index 320db8e9608..dcedc0b8c75 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/phases/UpdateStatusJobTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/phases/UpdateStatusJobTest.java @@ -25,6 +25,7 @@ import org.sonar.api.database.model.Snapshot; import org.sonar.batch.ServerMetadata; import org.sonar.batch.index.DefaultResourcePersister; import org.sonar.jpa.test.AbstractDbUnitTestCase; +import org.sonar.persistence.resource.ResourceIndexerDao; import javax.persistence.Query; @@ -50,7 +51,7 @@ public class UpdateStatusJobTest extends AbstractDbUnitTestCase { private void assertAnalysis(int snapshotId, String fixture) { setupData("sharedFixture", fixture); DatabaseSession session = getSession(); - UpdateStatusJob sensor = new UpdateStatusJob(mock(ServerMetadata.class), session, new DefaultResourcePersister(session), loadSnapshot(snapshotId)); + UpdateStatusJob sensor = new UpdateStatusJob(mock(ServerMetadata.class), session, new DefaultResourcePersister(session, mock(ResourceIndexerDao.class)), loadSnapshot(snapshotId)); sensor.execute(); getSession().stop(); diff --git a/sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexDto.java b/sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexDto.java index 5272747b0ec..e1622a105ba 100644 --- a/sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexDto.java +++ b/sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexDto.java @@ -25,7 +25,7 @@ public final class ResourceIndexDto { private int position; private int nameSize; private int resourceId; - private int projectId; + private int rootProjectId; public String getKey() { return key; @@ -54,12 +54,12 @@ public final class ResourceIndexDto { return this; } - public int getProjectId() { - return projectId; + public int getRootProjectId() { + return rootProjectId; } - public ResourceIndexDto setProjectId(int i) { - this.projectId = i; + public ResourceIndexDto setRootProjectId(int i) { + this.rootProjectId = i; return this; } diff --git a/sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexerDao.java b/sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexerDao.java index 485539a2e5a..3cc2900d356 100644 --- a/sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexerDao.java +++ b/sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexerDao.java @@ -19,14 +19,18 @@ */ package org.sonar.persistence.resource; +import com.google.common.collect.Lists; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.ResultContext; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.SqlSession; +import org.sonar.persistence.DatabaseUtils; import org.sonar.persistence.MyBatis; +import java.util.List; + public class ResourceIndexerDao { public static final int MINIMUM_KEY_SIZE = 3; @@ -37,68 +41,117 @@ public class ResourceIndexerDao { this.mybatis = mybatis; } - void index(ResourceDto resource, SqlSession session) { - String name = resource.getName(); - if (StringUtils.isBlank(name)) { - return; - } - String normalizedName = normalize(name); - if (normalizedName.length() >= MINIMUM_KEY_SIZE) { - ResourceIndexerMapper mapper = session.getMapper(ResourceIndexerMapper.class); - - Integer rootId; - if (resource.getRootId() != null) { - ResourceDto root = mapper.selectRootId(resource.getRootId()); - if (root != null) { - rootId = (Integer) ObjectUtils.defaultIfNull(root.getRootId(), root.getId()); - } else { - rootId = resource.getRootId(); - } - } else { - rootId = resource.getId(); - } - - ResourceIndexDto dto = new ResourceIndexDto() - .setResourceId(resource.getId()) - .setProjectId(rootId) - .setNameSize(name.length()); - - for (int position = 0; position <= normalizedName.length() - MINIMUM_KEY_SIZE; position++) { - dto.setPosition(position); - dto.setKey(StringUtils.substring(normalizedName, position)); - mapper.insert(dto); - } - - session.commit(); - } - } - - public void index(String resourceName, int resourceId, int projectId) { + public ResourceIndexerDao index(String resourceName, int resourceId, int rootProjectId) { SqlSession sqlSession = mybatis.openSession(); try { - index(new ResourceDto().setId(resourceId).setName(resourceName).setRootId(projectId), sqlSession); + index(new ResourceDto().setId(resourceId).setName(resourceName).setRootId(rootProjectId), sqlSession, true); } finally { sqlSession.close(); } + return this; } - public void index(ResourceIndexerFilter filter) { + public ResourceIndexerDao index(ResourceIndexerFilter filter) { final SqlSession sqlSession = mybatis.openSession(ExecutorType.BATCH); try { sqlSession.select("selectResourcesToIndex", filter, new ResultHandler() { public void handleResult(ResultContext context) { ResourceDto resource = (ResourceDto) context.getResultObject(); - index(resource, sqlSession); + + // The column PROJECTS.ROOT_ID references the module but not the root project in a multi-modules project. + boolean correctRootProjectId = false; + + index(resource, sqlSession, correctRootProjectId); } }); } finally { sqlSession.close(); } + return this; + } + + public ResourceIndexerDao delete(List<Integer> resourceIds) { + final SqlSession sqlSession = mybatis.openSession(); + try { + ResourceIndexerMapper mapper = sqlSession.getMapper(ResourceIndexerMapper.class); + List<List<Integer>> partitionsOfResourceIds = Lists.partition(resourceIds, DatabaseUtils.MAX_IN_ELEMENTS); + for (List<Integer> partitionOfResourceIds : partitionsOfResourceIds) { + if (!partitionOfResourceIds.isEmpty()) { + mapper.deleteByResourceIds(partitionOfResourceIds); + } + } + sqlSession.commit(); + + } finally { + sqlSession.close(); + } + return this; + } + + void index(ResourceDto resource, SqlSession session, boolean correctProjectRootId) { + String name = resource.getName(); + if (StringUtils.isBlank(name) || resource.getId() == null) { + return; + } + + String key = toKey(name); + if (key.length() >= MINIMUM_KEY_SIZE) { + ResourceIndexerMapper mapper = session.getMapper(ResourceIndexerMapper.class); + boolean toBeIndexed = sanitizeIndex(resource, key, mapper); + if (toBeIndexed) { + + ResourceIndexDto dto = new ResourceIndexDto() + .setResourceId(resource.getId()) + .setRootProjectId(loadRootProjectId(resource, mapper, correctProjectRootId)) + .setNameSize(name.length()); + + for (int position = 0; position <= key.length() - MINIMUM_KEY_SIZE; position++) { + dto.setPosition(position); + dto.setKey(StringUtils.substring(key, position)); + mapper.insert(dto); + } + + session.commit(); + } + } + } + + private Integer loadRootProjectId(ResourceDto resource, ResourceIndexerMapper mapper, boolean correctProjectRootId) { + if (correctProjectRootId) { + return resource.getRootId(); + } + Integer rootId; + if (resource.getRootId() != null) { + ResourceDto root = mapper.selectRootId(resource.getRootId()); + if (root != null) { + rootId = (Integer) ObjectUtils.defaultIfNull(root.getRootId(), root.getId()); + } else { + rootId = resource.getRootId(); + } + } else { + rootId = resource.getId(); + } + return rootId; + } + + /** + * Return true if the resource must be indexed, false if the resource is already indexed. + * If the resource is indexed with a different key, then this index is dropped and the + * resource must be indexed again. + */ + private boolean sanitizeIndex(ResourceDto resource, String key, ResourceIndexerMapper mapper) { + ResourceIndexDto masterIndex = mapper.selectMasterIndexByResourceId(resource.getId()); + if (masterIndex != null && !StringUtils.equals(key, masterIndex.getKey())) { + // resource has been renamed -> drop existing indexes + mapper.deleteByResourceId(resource.getId()); + masterIndex = null; + } + return masterIndex == null; } - static String normalize(String input) { + static String toKey(String input) { return StringUtils.lowerCase(input); } } diff --git a/sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexerMapper.java b/sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexerMapper.java index 209126ce466..beec37e4bfd 100644 --- a/sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexerMapper.java +++ b/sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexerMapper.java @@ -19,9 +19,19 @@ */ package org.sonar.persistence.resource; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + public interface ResourceIndexerMapper { ResourceDto selectRootId(int id); + ResourceIndexDto selectMasterIndexByResourceId(int resourceId); + + void deleteByResourceId(int resourceId); + + void deleteByResourceIds(@Param("resourceIds") List<Integer> resourceIds); + void insert(ResourceIndexDto dto); } diff --git a/sonar-core/src/main/resources/org/sonar/persistence/resource/ResourceIndexerMapper.xml b/sonar-core/src/main/resources/org/sonar/persistence/resource/ResourceIndexerMapper.xml index 5b0c389d3d1..94ac6e57a95 100644 --- a/sonar-core/src/main/resources/org/sonar/persistence/resource/ResourceIndexerMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/persistence/resource/ResourceIndexerMapper.xml @@ -3,7 +3,7 @@ <mapper namespace="org.sonar.persistence.resource.ResourceIndexerMapper"> - <select id="selectResourcesToIndex" parameterType="map" resultType="resource"> + <select id="selectResourcesToIndex" parameterType="map" resultType="Resource"> select id, root_id as "rootId", name from projects <where> @@ -18,16 +18,33 @@ </where> </select> - <select id="selectRootId" parameterType="int" resultType="resource"> + <select id="selectRootId" parameterType="int" resultType="Resource"> select id, root_id as "rootId" from projects where id=#{id} </select> + <select id="selectMasterIndexByResourceId" parameterType="int" resultType="ResourceIndex"> + select kee as "key", resource_id as "resourceId" + from resource_index + where resource_id=#{id} and position=0 + </select> + + <delete id="deleteByResourceId" parameterType="int"> + delete from resource_index + where resource_id=#{id} + </delete> + + <delete id="deleteByResourceIds" parameterType="map"> + delete from resource_index + where resource_id in + <foreach item="i" index="index" collection="resourceIds" open="(" separator="," close=")">#{i}</foreach> + </delete> + <insert id="insert" parameterType="ResourceIndex" useGeneratedKeys="false"> - insert into resource_index (kee, position, name_size, resource_id, project_id) - values (#{key}, #{position}, #{nameSize}, #{resourceId}, #{projectId}) + insert into resource_index (kee, position, name_size, resource_id, root_project_id) + values (#{key}, #{position}, #{nameSize}, #{resourceId}, #{rootProjectId}) </insert> </mapper> diff --git a/sonar-core/src/main/resources/org/sonar/persistence/schema-derby.ddl b/sonar-core/src/main/resources/org/sonar/persistence/schema-derby.ddl index 02ae63063d2..879e426a883 100644 --- a/sonar-core/src/main/resources/org/sonar/persistence/schema-derby.ddl +++ b/sonar-core/src/main/resources/org/sonar/persistence/schema-derby.ddl @@ -460,11 +460,11 @@ CREATE TABLE "LOADED_TEMPLATES" ( ); CREATE TABLE "RESOURCE_INDEX" ( - "KEE" VARCHAR(100) NOT NULL, + "KEE" VARCHAR(400) NOT NULL, "POSITION" INTEGER NOT NULL, "NAME_SIZE" INTEGER NOT NULL, "RESOURCE_ID" INTEGER NOT NULL, - "PROJECT_ID" INTEGER NOT NULL + "ROOT_PROJECT_ID" INTEGER NOT NULL ); CREATE TABLE "ACTION_PLANS" ( diff --git a/sonar-core/src/test/java/org/sonar/persistence/resource/ResourceIndexerDaoTest.java b/sonar-core/src/test/java/org/sonar/persistence/resource/ResourceIndexerDaoTest.java index 08eeaf64efe..3ee2e28b5a3 100644 --- a/sonar-core/src/test/java/org/sonar/persistence/resource/ResourceIndexerDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/persistence/resource/ResourceIndexerDaoTest.java @@ -23,6 +23,8 @@ import org.junit.Before; import org.junit.Test; import org.sonar.persistence.DaoTestCase; +import java.util.Arrays; + public class ResourceIndexerDaoTest extends DaoTestCase { private static ResourceIndexerDao dao; @@ -33,21 +35,47 @@ public class ResourceIndexerDaoTest extends DaoTestCase { } @Test - public void testIndex() { - setupData("testIndex"); + public void shouldIndexSingleResource() { + setupData("shouldIndexSingleResource"); dao.index("ZipUtils", 10, 8); - checkTables("testIndex", "resource_index"); + checkTables("shouldIndexSingleResource", "resource_index"); } @Test - public void testIndexAll() { - setupData("testIndexAll"); + public void shouldIndexAllResources() { + setupData("shouldIndexAllResources"); dao.index(ResourceIndexerFilter.create()); - checkTables("testIndexAll", "resource_index"); + checkTables("shouldIndexAllResources", "resource_index"); } + @Test + public void shouldIndexMultiModulesProject() { + setupData("shouldIndexMultiModulesProject"); + + dao.index(ResourceIndexerFilter.create()); + + checkTables("shouldIndexMultiModulesProject", "resource_index"); + } + + @Test + public void shouldReindexProjectAfterRenaming() { + setupData("shouldReindexProjectAfterRenaming"); + + dao.index(ResourceIndexerFilter.create()); + + checkTables("shouldReindexProjectAfterRenaming", "resource_index"); + } + + @Test + public void shouldDeleteIndexes() { + setupData("shouldDeleteIndexes"); + + dao.delete(Arrays.asList(3, 4, 5, 6)); + + checkTables("shouldDeleteIndexes", "resource_index"); + } } diff --git a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldDeleteIndexes-result.xml b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldDeleteIndexes-result.xml new file mode 100644 index 00000000000..b863399fac1 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldDeleteIndexes-result.xml @@ -0,0 +1,8 @@ +<dataset> + + <resource_index kee="struts" position="0" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="truts" position="1" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="ruts" position="2" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="uts" position="3" name_size="6" resource_id="1" root_project_id="1"/> + +</dataset> diff --git a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldDeleteIndexes.xml b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldDeleteIndexes.xml new file mode 100644 index 00000000000..535d54380cb --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldDeleteIndexes.xml @@ -0,0 +1,31 @@ +<dataset> + + <!-- Struts --> + <resource_index kee="struts" position="0" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="truts" position="1" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="ruts" position="2" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="uts" position="3" name_size="6" resource_id="1" root_project_id="1"/> + + <!-- RequestContext --> + <resource_index kee="requestcontext" position="0" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="equestcontext" position="1" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="questcontext" position="2" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="uestcontext" position="3" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="estcontext" position="4" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="stcontext" position="5" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="tcontext" position="6" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="context" position="7" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="ontext" position="8" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="ntext" position="9" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="text" position="10" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="ext" position="11" name_size="14" resource_id="3" root_project_id="1"/> + + <!-- ZipUtils --> + <resource_index kee="ziputils" position="0" name_size="8" resource_id="6" root_project_id="1"/> + <resource_index kee="iputils" position="1" name_size="8" resource_id="6" root_project_id="1"/> + <resource_index kee="putils" position="2" name_size="8" resource_id="6" root_project_id="1"/> + <resource_index kee="utils" position="3" name_size="8" resource_id="6" root_project_id="1"/> + <resource_index kee="tils" position="4" name_size="8" resource_id="6" root_project_id="1"/> + <resource_index kee="ils" position="5" name_size="8" resource_id="6" root_project_id="1"/> + +</dataset> diff --git a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/testIndexAll-result.xml b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexAllResources-result.xml index c1dd1bf9944..814c0cfbaca 100644 --- a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/testIndexAll-result.xml +++ b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexAllResources-result.xml @@ -11,22 +11,22 @@ enabled="[true]" language="java" copy_resource_id="[null]"/> <!-- Struts --> - <resource_index kee="struts" position="0" name_size="6" resource_id="1" project_id="1"/> - <resource_index kee="truts" position="1" name_size="6" resource_id="1" project_id="1"/> - <resource_index kee="ruts" position="2" name_size="6" resource_id="1" project_id="1"/> - <resource_index kee="uts" position="3" name_size="6" resource_id="1" project_id="1"/> + <resource_index kee="struts" position="0" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="truts" position="1" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="ruts" position="2" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="uts" position="3" name_size="6" resource_id="1" root_project_id="1"/> <!-- RequestContext --> - <resource_index kee="requestcontext" position="0" name_size="14" resource_id="3" project_id="1"/> - <resource_index kee="equestcontext" position="1" name_size="14" resource_id="3" project_id="1"/> - <resource_index kee="questcontext" position="2" name_size="14" resource_id="3" project_id="1"/> - <resource_index kee="uestcontext" position="3" name_size="14" resource_id="3" project_id="1"/> - <resource_index kee="estcontext" position="4" name_size="14" resource_id="3" project_id="1"/> - <resource_index kee="stcontext" position="5" name_size="14" resource_id="3" project_id="1"/> - <resource_index kee="tcontext" position="6" name_size="14" resource_id="3" project_id="1"/> - <resource_index kee="context" position="7" name_size="14" resource_id="3" project_id="1"/> - <resource_index kee="ontext" position="8" name_size="14" resource_id="3" project_id="1"/> - <resource_index kee="ntext" position="9" name_size="14" resource_id="3" project_id="1"/> - <resource_index kee="text" position="10" name_size="14" resource_id="3" project_id="1"/> - <resource_index kee="ext" position="11" name_size="14" resource_id="3" project_id="1"/> + <resource_index kee="requestcontext" position="0" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="equestcontext" position="1" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="questcontext" position="2" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="uestcontext" position="3" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="estcontext" position="4" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="stcontext" position="5" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="tcontext" position="6" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="context" position="7" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="ontext" position="8" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="ntext" position="9" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="text" position="10" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="ext" position="11" name_size="14" resource_id="3" root_project_id="1"/> </dataset> diff --git a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/testIndexAll.xml b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexAllResources.xml index f89a5e67f25..f89a5e67f25 100644 --- a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/testIndexAll.xml +++ b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexAllResources.xml diff --git a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexMultiModulesProject-result.xml b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexMultiModulesProject-result.xml new file mode 100644 index 00000000000..e009e317898 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexMultiModulesProject-result.xml @@ -0,0 +1,52 @@ +<dataset> + + <!-- project "struts" -> module "struts-core" -> file "RequestContext" --> + <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts" + root_id="[null]" + description="[null]" + enabled="[true]" language="java" copy_resource_id="[null]"/> + + <projects long_name="[null]" id="2" scope="PRJ" qualifier="BRC" kee="org.struts:struts-core" name="Struts Core" + root_id="1" + description="[null]" + enabled="[true]" language="java" copy_resource_id="[null]"/> + + <projects long_name="org.struts.RequestContext" id="3" scope="FIL" qualifier="CLA" kee="org.struts:struts-core:org.struts.RequestContext" + name="RequestContext" root_id="2" + description="[null]" + enabled="[true]" language="java" copy_resource_id="[null]"/> + + + <!-- The major goal is to test root_project_id --> + + <!-- Struts --> + <resource_index kee="struts" position="0" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="truts" position="1" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="ruts" position="2" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="uts" position="3" name_size="6" resource_id="1" root_project_id="1"/> + + <!-- Struts Core --> + <resource_index kee="struts core" position="0" name_size="11" resource_id="2" root_project_id="1"/> + <resource_index kee="truts core" position="1" name_size="11" resource_id="2" root_project_id="1"/> + <resource_index kee="ruts core" position="2" name_size="11" resource_id="2" root_project_id="1"/> + <resource_index kee="uts core" position="3" name_size="11" resource_id="2" root_project_id="1"/> + <resource_index kee="ts core" position="4" name_size="11" resource_id="2" root_project_id="1"/> + <resource_index kee="s core" position="5" name_size="11" resource_id="2" root_project_id="1"/> + <resource_index kee=" core" position="6" name_size="11" resource_id="2" root_project_id="1"/> + <resource_index kee="core" position="7" name_size="11" resource_id="2" root_project_id="1"/> + <resource_index kee="ore" position="8" name_size="11" resource_id="2" root_project_id="1"/> + + <!-- RequestContext --> + <resource_index kee="requestcontext" position="0" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="equestcontext" position="1" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="questcontext" position="2" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="uestcontext" position="3" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="estcontext" position="4" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="stcontext" position="5" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="tcontext" position="6" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="context" position="7" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="ontext" position="8" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="ntext" position="9" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="text" position="10" name_size="14" resource_id="3" root_project_id="1"/> + <resource_index kee="ext" position="11" name_size="14" resource_id="3" root_project_id="1"/> +</dataset> diff --git a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexMultiModulesProject.xml b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexMultiModulesProject.xml new file mode 100644 index 00000000000..4dabdc5beb8 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexMultiModulesProject.xml @@ -0,0 +1,19 @@ +<dataset> + + <!-- project "struts" -> module "struts-core" -> file "RequestContext" --> + <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts" + root_id="[null]" + description="[null]" + enabled="[true]" language="java" copy_resource_id="[null]"/> + + <projects long_name="[null]" id="2" scope="PRJ" qualifier="BRC" kee="org.struts:struts-core" name="Struts Core" + root_id="1" + description="[null]" + enabled="[true]" language="java" copy_resource_id="[null]"/> + + <projects long_name="org.struts.RequestContext" id="3" scope="FIL" qualifier="CLA" kee="org.struts:struts-core:org.struts.RequestContext" + name="RequestContext" root_id="2" + description="[null]" + enabled="[true]" language="java" copy_resource_id="[null]"/> + +</dataset> diff --git a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/testIndex-result.xml b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexSingleResource-result.xml index 3c1544c8f9a..f0b879c3925 100644 --- a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/testIndex-result.xml +++ b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexSingleResource-result.xml @@ -1,8 +1,8 @@ <dataset> - <resource_index kee="ziputils" position="0" name_size="8" resource_id="10" project_id="8"/> - <resource_index kee="iputils" position="1" name_size="8" resource_id="10" project_id="8"/> - <resource_index kee="putils" position="2" name_size="8" resource_id="10" project_id="8"/> - <resource_index kee="utils" position="3" name_size="8" resource_id="10" project_id="8"/> - <resource_index kee="tils" position="4" name_size="8" resource_id="10" project_id="8"/> - <resource_index kee="ils" position="5" name_size="8" resource_id="10" project_id="8"/> + <resource_index kee="ziputils" position="0" name_size="8" resource_id="10" root_project_id="8"/> + <resource_index kee="iputils" position="1" name_size="8" resource_id="10" root_project_id="8"/> + <resource_index kee="putils" position="2" name_size="8" resource_id="10" root_project_id="8"/> + <resource_index kee="utils" position="3" name_size="8" resource_id="10" root_project_id="8"/> + <resource_index kee="tils" position="4" name_size="8" resource_id="10" root_project_id="8"/> + <resource_index kee="ils" position="5" name_size="8" resource_id="10" root_project_id="8"/> </dataset>
\ No newline at end of file diff --git a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexSingleResource.xml b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexSingleResource.xml new file mode 100644 index 00000000000..5a4a28b7df3 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexSingleResource.xml @@ -0,0 +1 @@ +<dataset></dataset> diff --git a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldReindexProjectAfterRenaming-result.xml b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldReindexProjectAfterRenaming-result.xml new file mode 100644 index 00000000000..23893f22365 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldReindexProjectAfterRenaming-result.xml @@ -0,0 +1,21 @@ +<dataset> + + <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Apache Struts" + root_id="[null]" + description="[null]" + enabled="[true]" language="java" copy_resource_id="[null]"/> + + + <resource_index kee="apache struts" position="0" name_size="13" resource_id="1" root_project_id="1"/> + <resource_index kee="pache struts" position="1" name_size="13" resource_id="1" root_project_id="1"/> + <resource_index kee="ache struts" position="2" name_size="13" resource_id="1" root_project_id="1"/> + <resource_index kee="che struts" position="3" name_size="13" resource_id="1" root_project_id="1"/> + <resource_index kee="he struts" position="4" name_size="13" resource_id="1" root_project_id="1"/> + <resource_index kee="e struts" position="5" name_size="13" resource_id="1" root_project_id="1"/> + <resource_index kee=" struts" position="6" name_size="13" resource_id="1" root_project_id="1"/> + <resource_index kee="struts" position="7" name_size="13" resource_id="1" root_project_id="1"/> + <resource_index kee="truts" position="8" name_size="13" resource_id="1" root_project_id="1"/> + <resource_index kee="ruts" position="9" name_size="13" resource_id="1" root_project_id="1"/> + <resource_index kee="uts" position="10" name_size="13" resource_id="1" root_project_id="1"/> + +</dataset> diff --git a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldReindexProjectAfterRenaming.xml b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldReindexProjectAfterRenaming.xml new file mode 100644 index 00000000000..e29238680c8 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldReindexProjectAfterRenaming.xml @@ -0,0 +1,14 @@ +<dataset> + + <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Apache Struts" + root_id="[null]" + description="[null]" + enabled="[true]" language="java" copy_resource_id="[null]"/> + + <!-- the index is on the old name "Struts" but not on "Apache Struts --> + <resource_index kee="struts" position="0" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="truts" position="1" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="ruts" position="2" name_size="6" resource_id="1" root_project_id="1"/> + <resource_index kee="uts" position="3" name_size="6" resource_id="1" root_project_id="1"/> + +</dataset> diff --git a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/testIndex.xml b/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/testIndex.xml deleted file mode 100644 index 5ed00ba028b..00000000000 --- a/sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/testIndex.xml +++ /dev/null @@ -1 +0,0 @@ -<dataset></dataset>
\ No newline at end of file diff --git a/sonar-server/src/main/java/org/sonar/server/startup/IndexProjects.java b/sonar-server/src/main/java/org/sonar/server/startup/IndexProjects.java index c1013e1a739..61bd7037c85 100644 --- a/sonar-server/src/main/java/org/sonar/server/startup/IndexProjects.java +++ b/sonar-server/src/main/java/org/sonar/server/startup/IndexProjects.java @@ -62,8 +62,8 @@ public class IndexProjects implements ServerComponent { private static ResourceIndexerFilter newFilter() { return ResourceIndexerFilter.create() - .setQualifiers(new String[]{Qualifiers.PROJECT, Qualifiers.VIEW, Qualifiers.SUBVIEW}) - .setScopes(new String[]{Scopes.PROJECT}); + .setQualifiers(new String[]{Qualifiers.PROJECT, Qualifiers.VIEW, Qualifiers.SUBVIEW, Qualifiers.DIRECTORY, Qualifiers.PACKAGE, Qualifiers.FILE, Qualifiers.CLASS, Qualifiers.UNIT_TEST_FILE}) + .setScopes(new String[]{Scopes.PROJECT, Scopes.DIRECTORY, Scopes.FILE}); } } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/search_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/search_controller.rb index 1808fa1a1ea..af0531d546b 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/search_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/search_controller.rb @@ -30,7 +30,7 @@ class SearchController < ApplicationController def index @start_time = Time.now @search = params[:s] - unless @search.empty? + unless @search.blank? if @search.to_s.size>=3 normalized_search = @search.downcase @results = ResourceIndex.find(:all, diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/resource_index.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/resource_index.rb index 1db1a1cd361..bce4c3c954c 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/resource_index.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/resource_index.rb @@ -22,9 +22,9 @@ class ResourceIndex < ActiveRecord::Base set_table_name 'resource_index' belongs_to :resource, :class_name => 'Project', :foreign_key => 'resource_id' - belongs_to :project, :class_name => 'Project', :foreign_key => 'project_id' + belongs_to :root_project, :class_name => 'Project', :foreign_key => 'root_project_id' def resource_id_for_authorization - project_id + root_project_id end end
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/237_create_table_resource_index.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/237_create_table_resource_index.rb index c6190f0cfe6..c4093518e78 100644 --- a/sonar-server/src/main/webapp/WEB-INF/db/migrate/237_create_table_resource_index.rb +++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/237_create_table_resource_index.rb @@ -25,11 +25,11 @@ class CreateTableResourceIndex < ActiveRecord::Migration def self.up create_table 'resource_index', :id => false do |t| - t.column 'kee', :string, :null => false, :limit => 100 + t.column 'kee', :string, :null => false, :limit => 400 t.column 'position', :integer, :null => false t.column 'name_size', :integer, :null => false t.column 'resource_id', :integer, :null => false - t.column 'project_id', :integer, :null => false + t.column 'root_project_id', :integer, :null => false end add_index 'resource_index', 'kee', :name => 'resource_index_key' add_index 'resource_index', 'resource_id', :name => 'resource_index_rid' diff --git a/sonar-server/src/test/java/org/sonar/server/startup/IndexProjectsTest.java b/sonar-server/src/test/java/org/sonar/server/startup/IndexProjectsTest.java index 359d57f22ed..d87b036fdd4 100644 --- a/sonar-server/src/test/java/org/sonar/server/startup/IndexProjectsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/startup/IndexProjectsTest.java @@ -19,11 +19,8 @@ */ package org.sonar.server.startup; -import org.hamcrest.BaseMatcher; -import org.hamcrest.Description; import org.junit.Test; import org.sonar.api.platform.ServerUpgradeStatus; -import org.sonar.api.resources.Scopes; import org.sonar.jpa.entity.SchemaMigration; import org.sonar.persistence.resource.ResourceIndexerDao; import org.sonar.persistence.resource.ResourceIndexerFilter; @@ -67,14 +64,6 @@ public class IndexProjectsTest { new IndexProjects(status, indexerDao).start(); - verify(indexerDao).index(argThat(new BaseMatcher<ResourceIndexerFilter>() { - public boolean matches(Object o) { - ResourceIndexerFilter filter = (ResourceIndexerFilter) o; - return filter.getScopes().length == 1 && filter.getScopes()[0].equals(Scopes.PROJECT); - } - - public void describeTo(Description description) { - } - })); + verify(indexerDao).index(any(ResourceIndexerFilter.class)); } } |