aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java59
-rw-r--r--plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/api/DbCleanerCommands.java56
-rw-r--r--plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeDisabledResources.java24
-rw-r--r--plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanResources.java14
-rw-r--r--plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeDisabledResourcesTest.java6
-rw-r--r--plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanResourcesTest.java6
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java20
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/index/DefaultResourcePersisterTest.java14
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/phases/UpdateStatusJobTest.java3
-rw-r--r--sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexDto.java10
-rw-r--r--sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexerDao.java135
-rw-r--r--sonar-core/src/main/java/org/sonar/persistence/resource/ResourceIndexerMapper.java10
-rw-r--r--sonar-core/src/main/resources/org/sonar/persistence/resource/ResourceIndexerMapper.xml25
-rw-r--r--sonar-core/src/main/resources/org/sonar/persistence/schema-derby.ddl4
-rw-r--r--sonar-core/src/test/java/org/sonar/persistence/resource/ResourceIndexerDaoTest.java40
-rw-r--r--sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldDeleteIndexes-result.xml8
-rw-r--r--sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldDeleteIndexes.xml31
-rw-r--r--sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexAllResources-result.xml (renamed from sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/testIndexAll-result.xml)32
-rw-r--r--sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexAllResources.xml (renamed from sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/testIndexAll.xml)0
-rw-r--r--sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexMultiModulesProject-result.xml52
-rw-r--r--sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexMultiModulesProject.xml19
-rw-r--r--sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexSingleResource-result.xml (renamed from sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/testIndex-result.xml)12
-rw-r--r--sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldIndexSingleResource.xml1
-rw-r--r--sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldReindexProjectAfterRenaming-result.xml21
-rw-r--r--sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/shouldReindexProjectAfterRenaming.xml14
-rw-r--r--sonar-core/src/test/resources/org/sonar/persistence/resource/ResourceIndexerDaoTest/testIndex.xml1
-rw-r--r--sonar-server/src/main/java/org/sonar/server/startup/IndexProjects.java4
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/search_controller.rb2
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/models/resource_index.rb4
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/db/migrate/237_create_table_resource_index.rb4
-rw-r--r--sonar-server/src/test/java/org/sonar/server/startup/IndexProjectsTest.java13
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));
}
}