diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-01-28 18:06:14 +0100 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-02-02 20:50:17 +0100 |
commit | 7bcccf0899d7bfa9f4d1782b4f5699c1f896502f (patch) | |
tree | 33aad9ea69c0021d1c174baf707d8d9e79bc4e53 /sonar-core | |
parent | 8430a7ff14f49de0c395db1573cadea076c42779 (diff) | |
download | sonarqube-7bcccf0899d7bfa9f4d1782b4f5699c1f896502f.tar.gz sonarqube-7bcccf0899d7bfa9f4d1782b4f5699c1f896502f.zip |
SONAR-6113 Short-circuit sending of notifications when there are no subscribers
Diffstat (limited to 'sonar-core')
6 files changed, 150 insertions, 13 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java index abd7d3fb3d6..92f36946428 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java @@ -112,4 +112,14 @@ public final class DaoUtils { return results; } + public static String repeatCondition(String sql, int count, String separator) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < count; i++) { + sb.append(sql); + if (i < count-1) { + sb.append(" ").append(separator).append(" "); + } + } + return sb.toString(); + } } diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java index a3c29f4e0c0..0390d6fabdf 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java @@ -25,7 +25,11 @@ import com.google.common.io.Closeables; import org.apache.ibatis.builder.xml.XMLMapperBuilder; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.mapping.Environment; -import org.apache.ibatis.session.*; +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.transaction.jdbc.JdbcTransactionFactory; import org.apache.ibatis.type.JdbcType; import org.slf4j.LoggerFactory; @@ -44,7 +48,14 @@ import org.sonar.core.component.db.SnapshotMapper; import org.sonar.core.computation.db.AnalysisReportDto; import org.sonar.core.computation.db.AnalysisReportMapper; import org.sonar.core.config.Logback; -import org.sonar.core.dashboard.*; +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.dependency.DependencyDto; import org.sonar.core.dependency.DependencyMapper; import org.sonar.core.dependency.ResourceSnapshotDto; @@ -53,11 +64,33 @@ import org.sonar.core.duplication.DuplicationMapper; import org.sonar.core.duplication.DuplicationUnitDto; import org.sonar.core.graph.jdbc.GraphDto; import org.sonar.core.graph.jdbc.GraphDtoMapper; -import org.sonar.core.issue.db.*; -import org.sonar.core.measure.db.*; +import org.sonar.core.issue.db.ActionPlanDto; +import org.sonar.core.issue.db.ActionPlanMapper; +import org.sonar.core.issue.db.ActionPlanStatsDto; +import org.sonar.core.issue.db.ActionPlanStatsMapper; +import org.sonar.core.issue.db.BatchIssueDto; +import org.sonar.core.issue.db.IssueChangeDto; +import org.sonar.core.issue.db.IssueChangeMapper; +import org.sonar.core.issue.db.IssueDto; +import org.sonar.core.issue.db.IssueFilterDto; +import org.sonar.core.issue.db.IssueFilterFavouriteDto; +import org.sonar.core.issue.db.IssueFilterFavouriteMapper; +import org.sonar.core.issue.db.IssueFilterMapper; +import org.sonar.core.issue.db.IssueMapper; +import org.sonar.core.measure.db.MeasureDto; +import org.sonar.core.measure.db.MeasureFilterDto; +import org.sonar.core.measure.db.MeasureFilterMapper; +import org.sonar.core.measure.db.MeasureMapper; +import org.sonar.core.measure.db.MetricDto; +import org.sonar.core.measure.db.MetricMapper; import org.sonar.core.notification.db.NotificationQueueDto; import org.sonar.core.notification.db.NotificationQueueMapper; -import org.sonar.core.permission.*; +import org.sonar.core.permission.GroupWithPermissionDto; +import org.sonar.core.permission.PermissionTemplateDto; +import org.sonar.core.permission.PermissionTemplateGroupDto; +import org.sonar.core.permission.PermissionTemplateMapper; +import org.sonar.core.permission.PermissionTemplateUserDto; +import org.sonar.core.permission.UserWithPermissionDto; import org.sonar.core.persistence.dialect.Dialect; import org.sonar.core.persistence.migration.v44.Migration44Mapper; import org.sonar.core.persistence.migration.v45.Migration45Mapper; @@ -67,9 +100,22 @@ import org.sonar.core.properties.PropertyDto; import org.sonar.core.purge.IdUuidPair; import org.sonar.core.purge.PurgeMapper; import org.sonar.core.purge.PurgeableSnapshotDto; -import org.sonar.core.qualitygate.db.*; -import org.sonar.core.qualityprofile.db.*; -import org.sonar.core.resource.*; +import org.sonar.core.qualitygate.db.ProjectQgateAssociationDto; +import org.sonar.core.qualitygate.db.ProjectQgateAssociationMapper; +import org.sonar.core.qualitygate.db.QualityGateConditionDto; +import org.sonar.core.qualitygate.db.QualityGateConditionMapper; +import org.sonar.core.qualitygate.db.QualityGateDto; +import org.sonar.core.qualitygate.db.QualityGateMapper; +import org.sonar.core.qualityprofile.db.ActiveRuleDto; +import org.sonar.core.qualityprofile.db.ActiveRuleMapper; +import org.sonar.core.qualityprofile.db.ActiveRuleParamDto; +import org.sonar.core.qualityprofile.db.QualityProfileDto; +import org.sonar.core.qualityprofile.db.QualityProfileMapper; +import org.sonar.core.resource.ResourceDto; +import org.sonar.core.resource.ResourceIndexDto; +import org.sonar.core.resource.ResourceIndexerMapper; +import org.sonar.core.resource.ResourceKeyUpdaterMapper; +import org.sonar.core.resource.ResourceMapper; import org.sonar.core.rule.RuleDto; import org.sonar.core.rule.RuleMapper; import org.sonar.core.rule.RuleParamDto; @@ -79,7 +125,21 @@ import org.sonar.core.technicaldebt.db.CharacteristicMapper; import org.sonar.core.technicaldebt.db.RequirementMigrationDto; import org.sonar.core.template.LoadedTemplateDto; import org.sonar.core.template.LoadedTemplateMapper; -import org.sonar.core.user.*; +import org.sonar.core.user.AuthorDto; +import org.sonar.core.user.AuthorMapper; +import org.sonar.core.user.GroupDto; +import org.sonar.core.user.GroupMapper; +import org.sonar.core.user.GroupMembershipDto; +import org.sonar.core.user.GroupMembershipMapper; +import org.sonar.core.user.GroupRoleDto; +import org.sonar.core.user.RoleMapper; +import org.sonar.core.user.UserDto; +import org.sonar.core.user.UserGroupDto; +import org.sonar.core.user.UserGroupMapper; +import org.sonar.core.user.UserMapper; +import org.sonar.core.user.UserRoleDto; + +import javax.annotation.Nullable; import java.io.InputStream; @@ -98,7 +158,7 @@ public class MyBatis implements BatchComponent, ServerComponent { this.queue = queue; } - public static void closeQuietly(SqlSession session) { + public static void closeQuietly(@Nullable SqlSession session) { if (session != null) { try { session.close(); diff --git a/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java b/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java index 0ee8f546523..3f080f74beb 100644 --- a/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java +++ b/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java @@ -22,17 +22,24 @@ package org.sonar.core.properties; import com.google.common.base.Preconditions; import com.google.common.base.Strings; +import org.apache.commons.dbutils.DbUtils; import org.apache.commons.lang.StringUtils; import org.apache.ibatis.session.SqlSession; import org.sonar.api.BatchComponent; import org.sonar.api.ServerComponent; import org.sonar.api.resources.Scopes; import org.sonar.core.persistence.DaoComponent; +import org.sonar.core.persistence.DaoUtils; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import javax.annotation.Nullable; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -74,6 +81,33 @@ public class PropertiesDao implements BatchComponent, ServerComponent, DaoCompon } } + public boolean hasProjectNotificationSubscribersForDispatchers(String projectUuid, Collection<String> dispatcherKeys) { + DbSession session = mybatis.openSession(false); + Connection connection = session.getConnection(); + PreparedStatement pstmt = null; + ResultSet rs = null; + String sql = "SELECT count(*) FROM properties pp " + + "left outer join projects pj on pp.resource_id = pj.id " + + "where pp.user_id is not null and (pp.resource_id is null or pj.uuid=?) " + + "and (" + DaoUtils.repeatCondition("pp.prop_key like ?", dispatcherKeys.size(), "or") + ")"; + try { + pstmt = connection.prepareStatement(sql); + pstmt.setString(1, projectUuid); + int index = 2; + for (String dispatcherKey : dispatcherKeys) { + pstmt.setString(index, "notification." + dispatcherKey + ".%"); + index++; + } + rs = pstmt.executeQuery(); + return rs.next() && rs.getInt(1) > 0; + } catch (SQLException e) { + throw new IllegalStateException("Fail to execute SQL request: " + sql, e); + } finally { + DbUtils.closeQuietly(connection, pstmt, rs); + MyBatis.closeQuietly(session); + } + } + public List<PropertyDto> selectGlobalProperties() { SqlSession session = mybatis.openSession(false); try { diff --git a/sonar-core/src/test/java/org/sonar/core/persistence/DaoUtilsTest.java b/sonar-core/src/test/java/org/sonar/core/persistence/DaoUtilsTest.java index 7ef1161ef17..db891b609ec 100644 --- a/sonar-core/src/test/java/org/sonar/core/persistence/DaoUtilsTest.java +++ b/sonar-core/src/test/java/org/sonar/core/persistence/DaoUtilsTest.java @@ -33,4 +33,11 @@ public class DaoUtilsTest { assertThat(daoClasses).isNotEmpty(); } + + @Test + public void repeatCondition() throws Exception { + assertThat(DaoUtils.repeatCondition("uuid=?", 1, "or")).isEqualTo("uuid=?"); + assertThat(DaoUtils.repeatCondition("uuid=?", 3, "or")).isEqualTo("uuid=? or uuid=? or uuid=?"); + + } } diff --git a/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java b/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java index ab643acf4d6..c92c655006a 100644 --- a/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java @@ -29,6 +29,7 @@ import org.junit.rules.ExpectedException; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.persistence.DbSession; +import java.util.Arrays; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -39,8 +40,9 @@ public class PropertiesDaoTest extends AbstractDaoTestCase { @Rule public ExpectedException thrown = ExpectedException.none(); - private DbSession session; - private PropertiesDao dao; + + DbSession session; + PropertiesDao dao; @Before public void createDao() { @@ -104,6 +106,25 @@ public class PropertiesDaoTest extends AbstractDaoTestCase { } @Test + public void hasNotificationSubscribers() throws Exception { + setupData("findNotificationSubscribers"); + + // Nobody is subscribed + assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", Arrays.asList("NotSexyDispatcher"))).isFalse(); + + // Global subscribers + assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", Arrays.asList("DispatcherWithGlobalSubscribers"))).isTrue(); + + // Project subscribers + assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", Arrays.asList("DispatcherWithProjectSubscribers"))).isTrue(); + assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_B", Arrays.asList("DispatcherWithProjectSubscribers"))).isFalse(); + + // Global + Project subscribers + assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", Arrays.asList("DispatcherWithGlobalAndProjectSubscribers"))).isTrue(); + assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_B", Arrays.asList("DispatcherWithGlobalAndProjectSubscribers"))).isTrue(); + } + + @Test public void selectGlobalProperties() { setupData("selectGlobalProperties"); List<PropertyDto> properties = dao.selectGlobalProperties(); diff --git a/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/findNotificationSubscribers.xml b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/findNotificationSubscribers.xml index 78f7e4864dc..92f5da7324d 100644 --- a/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/findNotificationSubscribers.xml +++ b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/findNotificationSubscribers.xml @@ -10,8 +10,9 @@ login="simon" /> - <projects id="42" uuid="A" kee="org.apache:struts"/> + <projects id="42" uuid="PROJECT_A" kee="org.apache:struts"/> + <!-- global subscription --> <properties id="1" prop_key="notification.DispatcherWithGlobalSubscribers.Email" @@ -19,6 +20,7 @@ resource_id="[null]" user_id="2"/> + <!-- project subscription --> <properties id="2" prop_key="notification.DispatcherWithProjectSubscribers.Email" @@ -26,6 +28,7 @@ resource_id="42" user_id="1"/> + <!-- project subscription --> <properties id="3" prop_key="notification.DispatcherWithGlobalAndProjectSubscribers.Email" @@ -33,6 +36,7 @@ resource_id="56" user_id="1"/> + <!-- project subscription --> <properties id="4" prop_key="notification.DispatcherWithGlobalAndProjectSubscribers.Email" @@ -40,6 +44,7 @@ resource_id="42" user_id="1"/> + <!-- global subscription --> <properties id="5" prop_key="notification.DispatcherWithGlobalAndProjectSubscribers.Email" |