import org.sonar.api.config.Settings;
import org.sonar.api.resources.Scopes;
import org.sonar.core.purge.PurgeDao;
-import org.sonar.core.purge.PurgeSnapshotQuery;
import org.sonar.plugins.dbcleaner.api.DbCleanerConstants;
import org.sonar.plugins.dbcleaner.api.PurgeTask;
import org.sonar.plugins.dbcleaner.period.DefaultPeriodCleaner;
this.periodCleaner = periodCleaner;
}
- public PurgeTask purgeProject(long projectId) {
- cleanHistoricalData(projectId);
- deleteAbortedBuilds(projectId);
- deleteFileHistory(projectId);
- if (settings.getBoolean(DbCleanerConstants.PROPERTY_CLEAN_DIRECTORY)) {
- deleteDirectoryHistory(projectId);
- }
- purgeProjectResources(projectId);
+ public PurgeTask deleteProject(long projectId) {
+ purgeDao.deleteProject(projectId);
return this;
}
- public PurgeTask deleteProject(long projectId) {
- purgeDao.deleteProject(projectId);
+ public PurgeTask purgeProject(long projectId) {
+ cleanHistoricalData(projectId);
+ doPurgeProject(projectId);
return this;
}
}
}
- private void purgeProjectResources(long projectId) {
- try {
- LOG.debug("Purge project [id=" + projectId + "]");
- purgeDao.purgeProject(projectId);
- } catch (Exception e) {
- // purge errors must no fail the batch
- LOG.error("Fail to purge project [id=" + projectId + "]", e);
- }
- }
-
- private void deleteDirectoryHistory(long projectId) {
- try {
- LOG.debug("Delete historical data of directories [id=" + projectId + "]");
- PurgeSnapshotQuery query = PurgeSnapshotQuery.create()
- .setRootProjectId(projectId)
- .setIslast(false)
- .setScopes(new String[]{Scopes.DIRECTORY});
- purgeDao.deleteSnapshots(query);
- } catch (Exception e) {
- // purge errors must no fail the batch
- LOG.error("Fail to delete historical data of directories [id=" + projectId + "]", e);
- }
- }
-
- private void deleteFileHistory(long projectId) {
- try {
- LOG.debug("Delete historical data of files [id=" + projectId + "]");
- PurgeSnapshotQuery query = PurgeSnapshotQuery.create()
- .setRootProjectId(projectId)
- .setIslast(false)
- .setScopes(new String[]{Scopes.FILE});
- purgeDao.deleteSnapshots(query);
- } catch (Exception e) {
- // purge errors must no fail the batch
- LOG.error("Fail to delete historical data of files [id=" + projectId + "]", e);
+ private String[] getScopesWithoutHistoricalData() {
+ if (settings.getBoolean(DbCleanerConstants.PROPERTY_CLEAN_DIRECTORY)) {
+ return new String[]{Scopes.DIRECTORY, Scopes.FILE};
}
+ return new String[]{Scopes.FILE};
}
- private void deleteAbortedBuilds(long projectId) {
+ private void doPurgeProject(long projectId) {
try {
- LOG.debug("Delete aborted builds [id=" + projectId + "]");
- PurgeSnapshotQuery query = PurgeSnapshotQuery.create()
- .setRootProjectId(projectId)
- .setIslast(false)
- .setStatus(new String[]{"U"});
- purgeDao.deleteSnapshots(query);
+ LOG.debug("Purge project [id=" + projectId + "]");
+ purgeDao.purgeProject(projectId, getScopesWithoutHistoricalData());
} catch (Exception e) {
// purge errors must no fail the batch
- LOG.error("Fail to delete historical aborted builds [id=" + projectId + "]", e);
+ LOG.error("Fail to purge project [id=" + projectId + "]", e);
}
}
}
*/
package org.sonar.plugins.dbcleaner;
-import org.apache.commons.lang.ArrayUtils;
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
import org.junit.Test;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Scopes;
import org.sonar.core.purge.PurgeDao;
-import org.sonar.core.purge.PurgeSnapshotQuery;
import org.sonar.plugins.dbcleaner.api.DbCleanerConstants;
import org.sonar.plugins.dbcleaner.period.DefaultPeriodCleaner;
task.purgeProject(1L);
- verify(purgeDao, never()).deleteSnapshots(argThat(newDirectoryQueryMatcher()));
+ verify(purgeDao).purgeProject(1L, new String[]{Scopes.FILE});
}
@Test
task.purgeProject(1L);
- verify(purgeDao, times(1)).deleteSnapshots(argThat(newDirectoryQueryMatcher()));
+ verify(purgeDao).purgeProject(1L, new String[]{Scopes.DIRECTORY, Scopes.FILE});
}
@Test
public void shouldNotFailOnErrors() {
PurgeDao purgeDao = mock(PurgeDao.class);
- when(purgeDao.purgeProject(anyLong())).thenThrow(new RuntimeException());
+ when(purgeDao.purgeProject(anyLong(), (String[]) any())).thenThrow(new RuntimeException());
DefaultPurgeTask task = new DefaultPurgeTask(purgeDao, new Settings(), mock(DefaultPeriodCleaner.class));
task.purgeProject(1L);
- verify(purgeDao).purgeProject(anyLong());
- }
-
- private BaseMatcher<PurgeSnapshotQuery> newDirectoryQueryMatcher() {
- return new BaseMatcher<PurgeSnapshotQuery>() {
- public boolean matches(Object o) {
- return ArrayUtils.contains(((PurgeSnapshotQuery) o).getScopes(), "DIR");
- }
-
- public void describeTo(Description description) {
- description.appendText("Query on scope DIR");
- }
- };
+ verify(purgeDao).purgeProject(anyLong(), (String[]) any());
}
}
*/
package org.sonar.core.persistence;
-import java.io.IOException;
-import java.io.InputStream;
-
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.Environment;
-import org.apache.ibatis.session.Configuration;
-import org.apache.ibatis.session.ExecutorType;
-import org.apache.ibatis.session.SqlSession;
-import org.apache.ibatis.session.SqlSessionFactory;
-import org.apache.ibatis.session.SqlSessionFactoryBuilder;
+import org.apache.ibatis.session.*;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.slf4j.LoggerFactory;
import org.sonar.api.BatchComponent;
import org.sonar.api.ServerComponent;
-import org.sonar.core.dashboard.ActiveDashboardDto;
-import org.sonar.core.dashboard.ActiveDashboardMapper;
-import org.sonar.core.dashboard.DashboardDto;
-import org.sonar.core.dashboard.DashboardMapper;
-import org.sonar.core.dashboard.WidgetDto;
-import org.sonar.core.dashboard.WidgetMapper;
-import org.sonar.core.dashboard.WidgetPropertyDto;
-import org.sonar.core.dashboard.WidgetPropertyMapper;
+import org.sonar.core.dashboard.*;
import org.sonar.core.duplication.DuplicationMapper;
import org.sonar.core.duplication.DuplicationUnitDto;
import org.sonar.core.properties.PropertiesMapper;
import org.sonar.core.purge.PurgeMapper;
+import org.sonar.core.purge.PurgeVendorMapper;
import org.sonar.core.purge.PurgeableSnapshotDto;
-import org.sonar.core.resource.ResourceDto;
-import org.sonar.core.resource.ResourceIndexDto;
-import org.sonar.core.resource.ResourceIndexerMapper;
-import org.sonar.core.resource.ResourceMapper;
-import org.sonar.core.resource.SnapshotDto;
+import org.sonar.core.resource.*;
import org.sonar.core.review.ReviewDto;
import org.sonar.core.review.ReviewMapper;
import org.sonar.core.rule.RuleDto;
import org.sonar.core.template.LoadedTemplateDto;
import org.sonar.core.template.LoadedTemplateMapper;
+import java.io.IOException;
+import java.io.InputStream;
+
public class MyBatis implements BatchComponent, ServerComponent {
private Database database;
loadMapper(conf, LoadedTemplateMapper.class);
loadMapper(conf, PropertiesMapper.class);
loadMapper(conf, PurgeMapper.class);
+ loadMapper(conf, PurgeVendorMapper.class);
loadMapper(conf, ResourceMapper.class);
loadMapper(conf, ReviewMapper.class);
loadMapper(conf, ResourceIndexerMapper.class);
private InputStream getPathToMapper(Class mapperClass) {
InputStream input = getClass().getResourceAsStream(
- "/" + StringUtils.replace(mapperClass.getName(), ".", "/") + "-" + database.getDialect().getId() + ".xml");
+ "/" + StringUtils.replace(mapperClass.getName(), ".", "/") + "-" + database.getDialect().getId() + ".xml");
if (input == null) {
input = getClass().getResourceAsStream("/" + StringUtils.replace(mapperClass.getName(), ".", "/") + ".xml");
}
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
+import org.apache.commons.lang.ArrayUtils;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.SqlSession;
this.resourceDao = resourceDao;
}
- public PurgeDao purgeProject(long rootProjectId) {
+ public PurgeDao purgeProject(long rootProjectId, String[] scopesWithoutHistoricalData) {
SqlSession session = mybatis.openBatchSession();
PurgeMapper purgeMapper = session.getMapper(PurgeMapper.class);
try {
- List<Long> projectIds = Lists.newArrayList(rootProjectId);
- projectIds.addAll(resourceDao.getDescendantProjectIds(rootProjectId, session));
+ List<Long> projectIds = getProjectIds(rootProjectId, session);
for (Long projectId : projectIds) {
+ deleteAbortedBuilds(projectId, session, purgeMapper);
+ deleteHistoricalData(projectId, scopesWithoutHistoricalData, session, purgeMapper);
purgeProject(projectId, session, purgeMapper);
}
return this;
}
+ private void deleteAbortedBuilds(Long projectId, SqlSession session, PurgeMapper purgeMapper) {
+ PurgeSnapshotQuery query = PurgeSnapshotQuery.create()
+ .setIslast(false)
+ .setStatus(new String[]{"U"})
+ .setRootProjectId(projectId);
+ deleteSnapshots(query, session, purgeMapper);
+ }
+
+ private void deleteHistoricalData(Long projectId, String[] scopesWithoutHistoricalData, SqlSession session, PurgeMapper purgeMapper) {
+ if (!ArrayUtils.isEmpty(scopesWithoutHistoricalData)) {
+ PurgeSnapshotQuery query = PurgeSnapshotQuery.create()
+ .setIslast(false)
+ .setScopes(scopesWithoutHistoricalData)
+ .setRootProjectId(projectId);
+ deleteSnapshots(query, session, purgeMapper);
+ }
+ }
+
private void purgeProject(final Long projectId, final SqlSession session, final PurgeMapper purgeMapper) {
List<Long> projectSnapshotIds = purgeMapper.selectSnapshotIds(PurgeSnapshotQuery.create().setResourceId(projectId).setIslast(false).setNotPurged(true));
for (final Long projectSnapshotId : projectSnapshotIds) {
public PurgeDao deleteProject(long rootProjectId) {
final SqlSession session = mybatis.openBatchSession();
final PurgeMapper mapper = session.getMapper(PurgeMapper.class);
+ final PurgeVendorMapper vendorMapper = session.getMapper(PurgeVendorMapper.class);
try {
- deleteProject(rootProjectId, session, mapper);
+ deleteProject(rootProjectId, session, mapper, vendorMapper);
return this;
} finally {
MyBatis.closeQuietly(session);
}
}
- private void deleteProject(final long rootProjectId, final SqlSession session, final PurgeMapper mapper) {
+ private void deleteProject(final long rootProjectId, final SqlSession session, final PurgeMapper mapper, final PurgeVendorMapper vendorMapper) {
List<Long> childrenIds = mapper.selectProjectIdsByRootId(rootProjectId);
for (Long childId : childrenIds) {
- deleteProject(childId, session, mapper);
+ deleteProject(childId, session, mapper, vendorMapper);
}
session.select("org.sonar.core.purge.PurgeMapper.selectResourceTreeIdsByRootId", rootProjectId, new ResultHandler() {
public void handleResult(ResultContext context) {
Long resourceId = (Long) context.getResultObject();
- deleteResource(resourceId, session, mapper);
+ deleteResource(resourceId, session, mapper, vendorMapper);
}
});
session.commit();
}
- void deleteResource(final long resourceId, final SqlSession session, final PurgeMapper mapper) {
+ void deleteResource(final long resourceId, final SqlSession session, final PurgeMapper mapper, final PurgeVendorMapper vendorMapper) {
session.select("org.sonar.core.purge.PurgeMapper.selectSnapshotIdsByResource", resourceId, new ResultHandler() {
public void handleResult(ResultContext context) {
Long snapshotId = (Long) context.getResultObject();
deleteSnapshot(snapshotId, mapper);
}
});
- // TODO optimization: filter requests according to resource scope
+ // possible optimization: filter requests according to resource scope
mapper.deleteResourceLinks(resourceId);
mapper.deleteResourceProperties(resourceId);
mapper.deleteResourceIndex(resourceId);
mapper.deleteResourceUserRoles(resourceId);
mapper.deleteResourceManualMeasures(resourceId);
mapper.deleteResourceReviews(resourceId);
- mapper.deleteResourceReviewComments(resourceId);
- mapper.deleteResourceActionPlansReviews(resourceId);
+ vendorMapper.deleteResourceReviewComments(resourceId);
+ vendorMapper.deleteResourceActionPlansReviews(resourceId);
mapper.deleteResourceActionPlans(resourceId);
mapper.deleteResourceEvents(resourceId);
mapper.deleteResource(resourceId);
mapper.closeResourceReviews(resourceId);
}
-
public PurgeDao deleteSnapshots(PurgeSnapshotQuery query) {
final SqlSession session = mybatis.openBatchSession();
try {
final PurgeMapper mapper = session.getMapper(PurgeMapper.class);
- session.select("org.sonar.core.purge.PurgeMapper.selectSnapshotIds", query, new ResultHandler() {
- public void handleResult(ResultContext context) {
- Long snapshotId = (Long) context.getResultObject();
- deleteSnapshot(snapshotId, mapper);
- }
- });
+ deleteSnapshots(query, session, mapper);
session.commit();
return this;
}
}
+ private void deleteSnapshots(PurgeSnapshotQuery query, SqlSession session, final PurgeMapper mapper) {
+ session.select("org.sonar.core.purge.PurgeMapper.selectSnapshotIds", query, new ResultHandler() {
+ public void handleResult(ResultContext context) {
+ Long snapshotId = (Long) context.getResultObject();
+ deleteSnapshot(snapshotId, mapper);
+ }
+ });
+ }
+
+ /**
+ * Load the whole tree of projects, including the project given in parameter.
+ */
+ private List<Long> getProjectIds(long rootProjectId, SqlSession session) {
+ List<Long> projectIds = Lists.newArrayList(rootProjectId);
+ projectIds.addAll(resourceDao.getDescendantProjectIds(rootProjectId, session));
+ return projectIds;
+ }
+
@VisibleForTesting
void purgeSnapshot(long snapshotId, PurgeMapper mapper) {
// note that events are not deleted
void deleteResourceReviews(long resourceId);
- void deleteResourceReviewComments(long resourceId);
-
void deleteResourceEvents(long resourceId);
void deleteResourceActionPlans(long resourceId);
- void deleteResourceActionPlansReviews(long resourceId);
-
void closeResourceReviews(long resourceId);
List<PurgeableSnapshotDto> selectPurgeableSnapshotsWithEvents(long resourceId);
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.core.purge;
+
+/**
+ * Purge requests that are specific to database vendors. They are extracted from PurgeMapper
+ * as long as MyBatis does not support multiple databases in the same XML file.
+ *
+ * See code.google.com/p/mybatis/issues/detail?id=21
+ */
+public interface PurgeVendorMapper {
+ void deleteResourceReviewComments(long resourceId);
+
+ void deleteResourceActionPlansReviews(long resourceId);
+}
delete from reviews where resource_id=#{id}
</delete>
- <delete id="deleteResourceReviewComments" parameterType="long">
- delete from review_comments rc where exists (select * from reviews r where rc.review_id=r.id and r.resource_id=#{id})
- </delete>
-
<delete id="deleteResourceEvents" parameterType="long">
delete from events where resource_id=#{id}
</delete>
delete from action_plans where project_id=#{id}
</delete>
- <delete id="deleteResourceActionPlansReviews" parameterType="long">
- delete from action_plans_reviews apr where exists (select * from action_plans ap where ap.id=apr.action_plan_id and ap.project_id=#{id})
- </delete>
-
<update id="setSnapshotIsLastToFalse" parameterType="long">
update snapshots set islast=${_false} where project_id=#{id}
</update>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.core.purge.PurgeVendorMapper">
+
+ <delete id="deleteResourceReviewComments" parameterType="long">
+ delete from review_comments rc where exists (select * from reviews r where rc.review_id=r.id and
+ r.resource_id=#{id})
+ </delete>
+
+ <delete id="deleteResourceActionPlansReviews" parameterType="long">
+ delete from action_plans_reviews apr where exists (select * from action_plans ap where ap.id=apr.action_plan_id and
+ ap.project_id=#{id})
+ </delete>
+
+</mapper>
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.core.purge.PurgeVendorMapper">
+
+ <delete id="deleteResourceReviewComments" parameterType="long">
+ delete from review_comments using reviews where review_comments.review_id=reviews.id and reviews.resource_id=#{id}
+ </delete>
+
+ <delete id="deleteResourceActionPlansReviews" parameterType="long">
+ delete from action_plans_reviews using action_plans where action_plans.id=action_plans_reviews.action_plan_id and action_plans.project_id=#{id}
+ </delete>
+
+</mapper>
+
import org.hamcrest.Description;
import org.junit.Before;
import org.junit.Test;
+import org.sonar.api.resources.Scopes;
import org.sonar.core.persistence.DaoTestCase;
import org.sonar.core.persistence.MyBatis;
import org.sonar.core.resource.ResourceDao;
@Test
public void shouldPurgeProject() {
setupData("shouldPurgeProject");
- dao.purgeProject(1);
+ dao.purgeProject(1, new String[0]);
checkTables("shouldPurgeProject", "projects", "snapshots");
}
@Test
public void shouldPurgeDirectoriesAndFiles() {
setupData("shouldPurgeDirectoriesAndFiles");
- dao.purgeProject(1);
+ dao.purgeProject(1, new String[]{Scopes.DIRECTORY, Scopes.FILE});
checkTables("shouldPurgeDirectoriesAndFiles", "projects", "snapshots");
}
@Test
public void shouldDisableResourcesWithoutLastSnapshot() {
setupData("shouldDisableResourcesWithoutLastSnapshot");
- dao.purgeProject(1);
+ dao.purgeProject(1, new String[0]);
checkTables("shouldDisableResourcesWithoutLastSnapshot", "projects", "snapshots");
}
SqlSession session = getMyBatis().openSession();
try {
// this method does not commit and close the session
- dao.deleteResource(1L, session, session.getMapper(PurgeMapper.class));
+ dao.deleteResource(1L, session, session.getMapper(PurgeMapper.class), session.getMapper(PurgeVendorMapper.class));
session.commit();
} finally {