aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2013-08-27 10:30:55 +0200
committerJulien HENRY <julien.henry@sonarsource.com>2013-08-27 10:30:55 +0200
commit08a18e07cc842e111f834a395da414fd516606ee (patch)
tree39e70c62837af8bb562242a413399109e52ed486
parent8d4325511265cae0947a763cf559b2514052e4b7 (diff)
downloadsonarqube-08a18e07cc842e111f834a395da414fd516606ee.tar.gz
sonarqube-08a18e07cc842e111f834a395da414fd516606ee.zip
SONAR-4524 Create notifications using batch insert
to improve performances
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/notification/SendIssueNotificationsPostJob.java9
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/notification/SendIssueNotificationsPostJobTest.java43
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/IssueNotifications.java27
-rw-r--r--sonar-core/src/main/java/org/sonar/core/notification/DefaultNotificationManager.java13
-rw-r--r--sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDao.java8
-rw-r--r--sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDto.java35
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/BatchSession.java2
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/notification/db/NotificationQueueMapper.xml8
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/IssueNotificationsTest.java27
-rw-r--r--sonar-core/src/test/java/org/sonar/core/notification/DefaultNotificationManagerTest.java2
-rw-r--r--sonar-core/src/test/java/org/sonar/core/notification/db/NotificationQueueDaoTest.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/notifications/NotificationManager.java20
12 files changed, 119 insertions, 77 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/notification/SendIssueNotificationsPostJob.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/notification/SendIssueNotificationsPostJob.java
index 0a3658a34a0..0cbe7456c6e 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/notification/SendIssueNotificationsPostJob.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/notification/SendIssueNotificationsPostJob.java
@@ -30,6 +30,9 @@ import org.sonar.batch.issue.IssueCache;
import org.sonar.core.DryRunIncompatible;
import org.sonar.core.issue.IssueNotifications;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
/**
* @since 3.6
*/
@@ -54,6 +57,7 @@ public class SendIssueNotificationsPostJob implements PostJob {
private void sendNotifications(Project project) {
int newIssues = 0;
IssueChangeContext context = IssueChangeContext.createScan(project.getAnalysisDate());
+ Map<DefaultIssue, Rule> shouldSentNotification = new LinkedHashMap<DefaultIssue, Rule>();
for (DefaultIssue issue : issueCache.all()) {
if (issue.isNew() && issue.resolution() == null) {
newIssues++;
@@ -62,10 +66,13 @@ public class SendIssueNotificationsPostJob implements PostJob {
Rule rule = ruleFinder.findByKey(issue.ruleKey());
// TODO warning - rules with status REMOVED are currently ignored, but should not
if (rule != null) {
- notifications.sendChanges(issue, context, rule, project, null);
+ shouldSentNotification.put(issue, rule);
}
}
}
+ if (!shouldSentNotification.isEmpty()) {
+ notifications.sendChanges(shouldSentNotification, context, project, null);
+ }
if (newIssues > 0) {
notifications.sendNewIssues(project, newIssues);
}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/notification/SendIssueNotificationsPostJobTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/notification/SendIssueNotificationsPostJobTest.java
index 736049d7ae8..82a1fe70ff4 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/notification/SendIssueNotificationsPostJobTest.java
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/notification/SendIssueNotificationsPostJobTest.java
@@ -21,6 +21,7 @@ package org.sonar.plugins.core.issue.notification;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.batch.SensorContext;
@@ -36,8 +37,17 @@ import org.sonar.batch.issue.IssueCache;
import org.sonar.core.issue.IssueNotifications;
import java.util.Arrays;
-
-import static org.mockito.Mockito.*;
+import java.util.Map;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class SendIssueNotificationsPostJobTest {
@@ -62,7 +72,7 @@ public class SendIssueNotificationsPostJobTest {
when(issueCache.all()).thenReturn(Arrays.asList(
new DefaultIssue().setNew(true),
new DefaultIssue().setNew(false)
- ));
+ ));
SendIssueNotificationsPostJob job = new SendIssueNotificationsPostJob(issueCache, notifications, ruleFinder);
job.executeOn(project, sensorContext);
@@ -75,7 +85,7 @@ public class SendIssueNotificationsPostJobTest {
when(project.getAnalysisDate()).thenReturn(DateUtils.parseDate("2013-05-18"));
when(issueCache.all()).thenReturn(Arrays.asList(
new DefaultIssue().setNew(false)
- ));
+ ));
SendIssueNotificationsPostJob job = new SendIssueNotificationsPostJob(issueCache, notifications, ruleFinder);
job.executeOn(project, sensorContext);
@@ -100,7 +110,7 @@ public class SendIssueNotificationsPostJobTest {
SendIssueNotificationsPostJob job = new SendIssueNotificationsPostJob(issueCache, notifications, ruleFinder);
job.executeOn(project, sensorContext);
- verify(notifications).sendChanges(eq(issue), any(IssueChangeContext.class), eq(rule), any(Component.class), (Component)isNull());
+ verify(notifications).sendChanges(argThat(matchMapOf(issue, rule)), any(IssueChangeContext.class), any(Component.class), (Component) isNull());
}
@Test
@@ -119,7 +129,7 @@ public class SendIssueNotificationsPostJobTest {
SendIssueNotificationsPostJob job = new SendIssueNotificationsPostJob(issueCache, notifications, ruleFinder);
job.executeOn(project, sensorContext);
- verify(notifications, never()).sendChanges(eq(issue), eq(changeContext), any(Rule.class), any(Component.class), any(Component.class));
+ verify(notifications, never()).sendChanges(argThat(matchMapOf(issue, null)), eq(changeContext), any(Component.class), any(Component.class));
}
@Test
@@ -139,6 +149,25 @@ public class SendIssueNotificationsPostJobTest {
SendIssueNotificationsPostJob job = new SendIssueNotificationsPostJob(issueCache, notifications, ruleFinder);
job.executeOn(project, sensorContext);
- verify(notifications, never()).sendChanges(eq(issue), eq(changeContext), any(Rule.class), any(Component.class), any(Component.class));
+ verify(notifications, never()).sendChanges(argThat(matchMapOf(issue, null)), eq(changeContext), any(Component.class), any(Component.class));
+ }
+
+ private static IsMapOfIssueAndRule matchMapOf(DefaultIssue issue, Rule rule) {
+ return new IsMapOfIssueAndRule(issue, rule);
+ }
+
+ static class IsMapOfIssueAndRule extends ArgumentMatcher<Map<DefaultIssue, Rule>> {
+ private DefaultIssue issue;
+ private Rule rule;
+
+ public IsMapOfIssueAndRule(DefaultIssue issue, Rule rule) {
+ this.issue = issue;
+ this.rule = rule;
+ }
+
+ public boolean matches(Object arg) {
+ Map map = (Map) arg;
+ return map.size() == 1 && map.get(issue) != null && map.get(issue).equals(rule);
+ }
}
}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/IssueNotifications.java b/sonar-core/src/main/java/org/sonar/core/issue/IssueNotifications.java
index 1a96d48446d..5619105e218 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/IssueNotifications.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/IssueNotifications.java
@@ -19,6 +19,8 @@
*/
package org.sonar.core.issue;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import org.sonar.api.BatchComponent;
import org.sonar.api.ServerComponent;
import org.sonar.api.component.Component;
@@ -35,8 +37,11 @@ import org.sonar.core.i18n.RuleI18nManager;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+
+import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Map.Entry;
/**
* Send notifications related to issues.
@@ -63,17 +68,23 @@ public class IssueNotifications implements BatchComponent, ServerComponent {
}
@CheckForNull
- public Notification sendChanges(DefaultIssue issue, IssueChangeContext context, IssueQueryResult queryResult) {
- return sendChanges(issue, context, queryResult.rule(issue), queryResult.project(issue), queryResult.component(issue));
+ public List<Notification> sendChanges(DefaultIssue issue, IssueChangeContext context, IssueQueryResult queryResult) {
+ Map<DefaultIssue, Rule> issues = Maps.newHashMap();
+ issues.put(issue, queryResult.rule(issue));
+ return sendChanges(issues, context, queryResult.project(issue), queryResult.component(issue));
}
@CheckForNull
- public Notification sendChanges(DefaultIssue issue, IssueChangeContext context, Rule rule, Component project, @Nullable Component component) {
- Notification notification = createChangeNotification(issue, context, rule, project, component, null);
- if (notification != null) {
- notificationsManager.scheduleForSending(notification);
+ public List<Notification> sendChanges(Map<DefaultIssue, Rule> issues, IssueChangeContext context, Component project, @Nullable Component component) {
+ List<Notification> notifications = Lists.newArrayList();
+ for (Entry<DefaultIssue, Rule> entry : issues.entrySet()) {
+ Notification notification = createChangeNotification(entry.getKey(), context, entry.getValue(), project, component, null);
+ if (notification != null) {
+ notifications.add(notification);
+ }
}
- return notification;
+ notificationsManager.scheduleForSending(notifications);
+ return notifications;
}
@CheckForNull
@@ -87,7 +98,7 @@ public class IssueNotifications implements BatchComponent, ServerComponent {
@CheckForNull
private Notification createChangeNotification(DefaultIssue issue, IssueChangeContext context, Rule rule, Component project,
- @Nullable Component component, @Nullable String comment) {
+ @Nullable Component component, @Nullable String comment) {
Notification notification = null;
if (comment != null || issue.mustSendNotifications()) {
FieldDiffs currentChange = issue.currentChange();
diff --git a/sonar-core/src/main/java/org/sonar/core/notification/DefaultNotificationManager.java b/sonar-core/src/main/java/org/sonar/core/notification/DefaultNotificationManager.java
index 6d8b17921a2..e57c182f566 100644
--- a/sonar-core/src/main/java/org/sonar/core/notification/DefaultNotificationManager.java
+++ b/sonar-core/src/main/java/org/sonar/core/notification/DefaultNotificationManager.java
@@ -20,7 +20,9 @@
package org.sonar.core.notification;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.SetMultimap;
import org.sonar.api.notifications.Notification;
@@ -66,7 +68,16 @@ public class DefaultNotificationManager implements NotificationManager {
*/
public void scheduleForSending(Notification notification) {
NotificationQueueDto dto = NotificationQueueDto.toNotificationQueueDto(notification);
- notificationQueueDao.insert(dto);
+ notificationQueueDao.insert(Arrays.asList(dto));
+ }
+
+ public void scheduleForSending(List<Notification> notification) {
+ notificationQueueDao.insert(Lists.transform(notification, new Function<Notification, NotificationQueueDto>() {
+ @Override
+ public NotificationQueueDto apply(Notification notification) {
+ return NotificationQueueDto.toNotificationQueueDto(notification);
+ }
+ }));
}
/**
diff --git a/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDao.java b/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDao.java
index 8bb11e157d8..1152e48937b 100644
--- a/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDao.java
+++ b/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDao.java
@@ -39,10 +39,12 @@ public class NotificationQueueDao implements BatchComponent, ServerComponent {
this.mybatis = mybatis;
}
- public void insert(NotificationQueueDto dto) {
- SqlSession session = mybatis.openSession();
+ public void insert(List<NotificationQueueDto> dtos) {
+ SqlSession session = mybatis.openBatchSession();
try {
- session.getMapper(NotificationQueueMapper.class).insert(dto);
+ for (NotificationQueueDto dto : dtos) {
+ session.getMapper(NotificationQueueMapper.class).insert(dto);
+ }
session.commit();
} finally {
MyBatis.closeQuietly(session);
diff --git a/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDto.java b/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDto.java
index 4e9668a507b..a42edc1a86e 100644
--- a/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDto.java
+++ b/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDto.java
@@ -31,7 +31,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
-import java.util.Date;
/**
* @since 4.0
@@ -39,7 +38,6 @@ import java.util.Date;
public class NotificationQueueDto {
private Long id;
- private Date createdAt;
private byte[] data;
public Long getId() {
@@ -51,15 +49,6 @@ public class NotificationQueueDto {
return this;
}
- public Date getCreatedAt() {
- return createdAt;
- }
-
- public NotificationQueueDto setCreatedAt(Date createdAt) {
- this.createdAt = createdAt;
- return this;
- }
-
public byte[] getData() {
return data;
}
@@ -70,24 +59,6 @@ public class NotificationQueueDto {
}
@Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- NotificationQueueDto actionPlanDto = (NotificationQueueDto) o;
- return !(id != null ? !id.equals(actionPlanDto.id) : actionPlanDto.id != null);
- }
-
- @Override
- public int hashCode() {
- return id != null ? id.hashCode() : 0;
- }
-
- @Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
@@ -101,7 +72,7 @@ public class NotificationQueueDto {
return new NotificationQueueDto().setData(byteArrayOutputStream.toByteArray());
} catch (IOException e) {
- throw new SonarException(e);
+ throw new SonarException("Unable to write notification", e);
} finally {
IOUtils.closeQuietly(byteArrayOutputStream);
@@ -121,10 +92,10 @@ public class NotificationQueueDto {
return (Notification) result;
} catch (IOException e) {
- throw new SonarException(e);
+ throw new SonarException("Unable to read notification", e);
} catch (ClassNotFoundException e) {
- throw new SonarException(e);
+ throw new SonarException("Unable to read notification", e);
} finally {
IOUtils.closeQuietly(byteArrayInputStream);
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/BatchSession.java b/sonar-core/src/main/java/org/sonar/core/persistence/BatchSession.java
index 739fbfdc974..d19383176c2 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/BatchSession.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/BatchSession.java
@@ -123,7 +123,7 @@ public final class BatchSession implements SqlSession {
if (null != mappedStatement) {
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
if (keyGenerator instanceof Jdbc3KeyGenerator) {
- throw new IllegalStateException("Batch updates cannot use generated keys");
+ throw new IllegalStateException("Batch inserts cannot use generated keys");
}
}
}
diff --git a/sonar-core/src/main/resources/org/sonar/core/notification/db/NotificationQueueMapper.xml b/sonar-core/src/main/resources/org/sonar/core/notification/db/NotificationQueueMapper.xml
index c14850598b2..eb933957b73 100644
--- a/sonar-core/src/main/resources/org/sonar/core/notification/db/NotificationQueueMapper.xml
+++ b/sonar-core/src/main/resources/org/sonar/core/notification/db/NotificationQueueMapper.xml
@@ -4,7 +4,7 @@
<mapper namespace="org.sonar.core.notification.db.NotificationQueueMapper">
- <insert id="insert" parameterType="NotificationQueue" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
+ <insert id="insert" parameterType="NotificationQueue" useGeneratedKeys="false">
INSERT INTO notifications (created_at, data)
VALUES (current_timestamp, #{data})
</insert>
@@ -14,7 +14,7 @@
</delete>
<select id="findOldest" parameterType="int" resultType="NotificationQueue">
- select id, created_at, data
+ select id, data
from notifications
order by created_at asc
limit #{count}
@@ -22,7 +22,7 @@
<!-- SQL Server -->
<select id="findOldest" parameterType="int" resultType="NotificationQueue" databaseId="mssql">
- select top (#{count}) id, created_at, data
+ select top (#{count}) id, data
from notifications
order by created_at asc
</select>
@@ -30,7 +30,7 @@
<!-- Oracle -->
<select id="findOldest" parameterType="int" resultType="NotificationQueue" databaseId="oracle">
select * from (select
- id, created_at, data
+ id, data
from notifications
order by created_at asc
)
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/IssueNotificationsTest.java b/sonar-core/src/test/java/org/sonar/core/issue/IssueNotificationsTest.java
index 9528e691d28..806dcea781a 100644
--- a/sonar-core/src/test/java/org/sonar/core/issue/IssueNotificationsTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/issue/IssueNotificationsTest.java
@@ -41,6 +41,7 @@ import java.util.Arrays;
import java.util.Date;
import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.eq;
@RunWith(MockitoJUnitRunner.class)
public class IssueNotificationsTest {
@@ -82,10 +83,10 @@ public class IssueNotificationsTest {
.setSendNotifications(true)
.setComponentKey("struts:Action")
.setProjectKey("struts");
- DefaultIssueQueryResult queryResult = new DefaultIssueQueryResult(Arrays.<Issue>asList(issue));
- queryResult.addProjects(Arrays.<Component>asList(new Project("struts")));
+ DefaultIssueQueryResult queryResult = new DefaultIssueQueryResult(Arrays.<Issue> asList(issue));
+ queryResult.addProjects(Arrays.<Component> asList(new Project("struts")));
- Notification notification = issueNotifications.sendChanges(issue, context, queryResult);
+ Notification notification = issueNotifications.sendChanges(issue, context, queryResult).get(0);
assertThat(notification.getFieldValue("message")).isEqualTo("the message");
assertThat(notification.getFieldValue("key")).isEqualTo("ABCDE");
@@ -97,7 +98,7 @@ public class IssueNotificationsTest {
assertThat(notification.getFieldValue("new.status")).isEqualTo("RESOLVED");
assertThat(notification.getFieldValue("old.assignee")).isEqualTo("simon");
assertThat(notification.getFieldValue("new.assignee")).isNull();
- Mockito.verify(manager).scheduleForSending(notification);
+ Mockito.verify(manager).scheduleForSending(eq(Arrays.asList(notification)));
}
@Test
@@ -109,8 +110,8 @@ public class IssueNotificationsTest {
.setAssignee("freddy")
.setComponentKey("struts:Action")
.setProjectKey("struts");
- DefaultIssueQueryResult queryResult = new DefaultIssueQueryResult(Arrays.<Issue>asList(issue));
- queryResult.addProjects(Arrays.<Component>asList(new Project("struts")));
+ DefaultIssueQueryResult queryResult = new DefaultIssueQueryResult(Arrays.<Issue> asList(issue));
+ queryResult.addProjects(Arrays.<Component> asList(new Project("struts")));
Notification notification = issueNotifications.sendChanges(issue, context, queryResult, "I don't know how to fix it?");
@@ -131,11 +132,11 @@ public class IssueNotificationsTest {
.setSendNotifications(true)
.setComponentKey("struts:Action")
.setProjectKey("struts");
- DefaultIssueQueryResult queryResult = new DefaultIssueQueryResult(Arrays.<Issue>asList(issue));
- queryResult.addProjects(Arrays.<Component>asList(new Project("struts")));
- queryResult.addComponents(Arrays.<Component>asList(new ResourceComponent(new File("struts:Action").setEffectiveKey("struts:Action"))));
+ DefaultIssueQueryResult queryResult = new DefaultIssueQueryResult(Arrays.<Issue> asList(issue));
+ queryResult.addProjects(Arrays.<Component> asList(new Project("struts")));
+ queryResult.addComponents(Arrays.<Component> asList(new ResourceComponent(new File("struts:Action").setEffectiveKey("struts:Action"))));
- Notification notification = issueNotifications.sendChanges(issue, context, queryResult);
+ Notification notification = issueNotifications.sendChanges(issue, context, queryResult).get(0);
assertThat(notification.getFieldValue("message")).isEqualTo("the message");
assertThat(notification.getFieldValue("key")).isEqualTo("ABCDE");
@@ -143,7 +144,7 @@ public class IssueNotificationsTest {
assertThat(notification.getFieldValue("componentName")).isEqualTo("struts:Action");
assertThat(notification.getFieldValue("old.resolution")).isNull();
assertThat(notification.getFieldValue("new.resolution")).isEqualTo("FIXED");
- Mockito.verify(manager).scheduleForSending(notification);
+ Mockito.verify(manager).scheduleForSending(eq(Arrays.asList(notification)));
}
@Test
@@ -154,8 +155,8 @@ public class IssueNotificationsTest {
.setKey("ABCDE")
.setComponentKey("struts:Action")
.setProjectKey("struts");
- DefaultIssueQueryResult queryResult = new DefaultIssueQueryResult(Arrays.<Issue>asList(issue));
- queryResult.addProjects(Arrays.<Component>asList(new Project("struts")));
+ DefaultIssueQueryResult queryResult = new DefaultIssueQueryResult(Arrays.<Issue> asList(issue));
+ queryResult.addProjects(Arrays.<Component> asList(new Project("struts")));
Notification notification = issueNotifications.sendChanges(issue, context, queryResult, null);
diff --git a/sonar-core/src/test/java/org/sonar/core/notification/DefaultNotificationManagerTest.java b/sonar-core/src/test/java/org/sonar/core/notification/DefaultNotificationManagerTest.java
index bdd0a264d4d..118d2a356ba 100644
--- a/sonar-core/src/test/java/org/sonar/core/notification/DefaultNotificationManagerTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/notification/DefaultNotificationManagerTest.java
@@ -88,7 +88,7 @@ public class DefaultNotificationManagerTest extends AbstractDbUnitTestCase {
Notification notification = new Notification("test");
manager.scheduleForSending(notification);
- verify(notificationQueueDao, only()).insert(any(NotificationQueueDto.class));
+ verify(notificationQueueDao, only()).insert(any(List.class));
}
@Test
diff --git a/sonar-core/src/test/java/org/sonar/core/notification/db/NotificationQueueDaoTest.java b/sonar-core/src/test/java/org/sonar/core/notification/db/NotificationQueueDaoTest.java
index ec1f37e82aa..6be6b8f913b 100644
--- a/sonar-core/src/test/java/org/sonar/core/notification/db/NotificationQueueDaoTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/notification/db/NotificationQueueDaoTest.java
@@ -43,7 +43,7 @@ public class NotificationQueueDaoTest extends AbstractDaoTestCase {
public void should_insert_new_notification_queue() {
NotificationQueueDto notificationQueueDto = NotificationQueueDto.toNotificationQueueDto(new Notification("email"));
- dao.insert(notificationQueueDto);
+ dao.insert(Arrays.asList(notificationQueueDto));
checkTables("should_insert_new_notification_queue", new String[] {"id", "created_at"}, "notifications");
assertThat(dao.findOldest(1).get(0).toNotification().getType()).isEqualTo("email");
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/notifications/NotificationManager.java b/sonar-plugin-api/src/main/java/org/sonar/api/notifications/NotificationManager.java
index 1200efbaf8a..3f9e24a9906 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/notifications/NotificationManager.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/notifications/NotificationManager.java
@@ -25,35 +25,45 @@ import org.sonar.api.ServerComponent;
import javax.annotation.Nullable;
+import java.util.List;
+
/**
* <p>
* The notification manager receives notifications and is in charge of storing them so that they are processed by the notification service.
* </p>
* <p>
- * Pico provides an instance of this class, and plugins just need to create notifications and pass them to this manager with
+ * Pico provides an instance of this class, and plugins just need to create notifications and pass them to this manager with
* the {@link NotificationManager#scheduleForSending(Notification)} method.
* </p>
- *
+ *
* @since 2.10
*/
public interface NotificationManager extends ServerComponent, BatchComponent {
/**
* Receives a notification and stores it so that it is processed by the notification service.
- *
+ *
* @param notification the notification.
*/
void scheduleForSending(Notification notification);
/**
+ * Receives notifications and stores them so that they are processed by the notification service.
+ *
+ * @param notifications the notifications.
+ * @since 4.0
+ */
+ void scheduleForSending(List<Notification> notifications);
+
+ /**
* <p>
- * Returns the list of users who subscribed to the given dispatcher, along with the notification channels (email, twitter, ...) that they choose
+ * Returns the list of users who subscribed to the given dispatcher, along with the notification channels (email, twitter, ...) that they choose
* for this dispatcher.
* </p>
* <p>
* The resource ID can be null in case of notifications that have nothing to do with a specific project (like system notifications).
* </p>
- *
+ *
* @param dispatcher the dispatcher for which this list of users is requested
* @param resourceId the optional resource which is concerned by this request
* @return the list of user login along with the subscribed channels