summaryrefslogtreecommitdiffstats
path: root/sonar-core
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2015-01-28 18:06:14 +0100
committerSimon Brandhof <simon.brandhof@sonarsource.com>2015-02-02 20:50:17 +0100
commit7bcccf0899d7bfa9f4d1782b4f5699c1f896502f (patch)
tree33aad9ea69c0021d1c174baf707d8d9e79bc4e53 /sonar-core
parent8430a7ff14f49de0c395db1573cadea076c42779 (diff)
downloadsonarqube-7bcccf0899d7bfa9f4d1782b4f5699c1f896502f.tar.gz
sonarqube-7bcccf0899d7bfa9f4d1782b4f5699c1f896502f.zip
SONAR-6113 Short-circuit sending of notifications when there are no subscribers
Diffstat (limited to 'sonar-core')
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java10
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java80
-rw-r--r--sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java34
-rw-r--r--sonar-core/src/test/java/org/sonar/core/persistence/DaoUtilsTest.java7
-rw-r--r--sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java25
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/findNotificationSubscribers.xml7
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"