aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2015-06-08 15:22:34 +0200
committerSimon Brandhof <simon.brandhof@sonarsource.com>2015-06-08 15:22:34 +0200
commitabbf32571232db81a5343db17a933a9ce6923b44 (patch)
treebe13ec6d05a79259e5c71c7c3b0eab3003adbbe0 /server
parent81d37f61213eb6d51e1185da984e7390e459c7a4 (diff)
downloadsonarqube-abbf32571232db81a5343db17a933a9ce6923b44.tar.gz
sonarqube-abbf32571232db81a5343db17a933a9ce6923b44.zip
SONAR-6567 move notifications from sonar-core to sonar-server
Diffstat (limited to 'server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/event/NewAlerts.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/notification/ChangesOnMyIssueNotificationDispatcher.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/notification/DoNotFixNotificationDispatcher.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesNotificationDispatcher.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcher.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java177
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/notification/NotificationCenter.java (renamed from server/sonar-server/src/main/java/org/sonar/server/notifications/NotificationCenter.java)3
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/notification/NotificationDispatcher.java130
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/notification/NotificationDispatcherMetadata.java102
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/notification/NotificationManager.java70
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/notification/NotificationService.java (renamed from server/sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java)4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/notification/email/AlertsEmailTemplate.java (renamed from server/sonar-server/src/main/java/org/sonar/server/notifications/email/AlertsEmailTemplate.java)2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/notification/email/EmailNotificationChannel.java (renamed from server/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java)2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/notification/email/package-info.java (renamed from server/sonar-server/src/main/java/org/sonar/server/notifications/email/package-info.java)2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/notification/package-info.java (renamed from server/sonar-server/src/main/java/org/sonar/server/notifications/package-info.java)2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java10
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/event/NewAlertsTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/notification/ChangesOnMyIssueNotificationDispatcherTest.java6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/notification/DoNotFixNotificationDispatcherTest.java6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/notification/MyNewIssuesNotificationDispatcherTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcherTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/notification/DefaultNotificationManagerTest.java180
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/notification/NotificationCenterTest.java (renamed from server/sonar-server/src/test/java/org/sonar/server/notifications/NotificationCenterTest.java)3
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/notification/NotificationChannelTest.java44
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/notification/NotificationDispatcherMetadataTest.java46
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/notification/NotificationDispatcherTest.java101
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/notification/NotificationServiceTest.java (renamed from server/sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java)4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/notification/NotificationTest.java (renamed from server/sonar-server/src/test/java/org/sonar/server/notifications/NotificationTest.java)2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/notification/email/AlertsEmailTemplateTest.java (renamed from server/sonar-server/src/test/java/org/sonar/server/notifications/email/AlertsEmailTemplateTest.java)2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/notification/email/EmailNotificationChannelTest.java (renamed from server/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java)2
34 files changed, 898 insertions, 54 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java
index cbc929306ea..5129e8a4959 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java
@@ -34,13 +34,13 @@ import org.sonar.server.issue.notification.MyNewIssuesNotification;
import org.sonar.server.issue.notification.NewIssuesNotification;
import org.sonar.server.issue.notification.NewIssuesNotificationFactory;
import org.sonar.server.issue.notification.NewIssuesStatistics;
-import org.sonar.server.notifications.NotificationService;
+import org.sonar.server.notification.NotificationService;
import org.sonar.server.util.CloseableIterator;
/**
* Reads issues from disk cache and send related notifications. For performance reasons,
* the standard notification DB queue is not used as a temporary storage. Notifications
- * are directly processed by {@link org.sonar.server.notifications.NotificationService}.
+ * are directly processed by {@link NotificationService}.
*/
public class SendIssueNotificationsStep implements ComputationStep {
/**
diff --git a/server/sonar-server/src/main/java/org/sonar/server/event/NewAlerts.java b/server/sonar-server/src/main/java/org/sonar/server/event/NewAlerts.java
index cf4c219e848..6ec224ec1f6 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/event/NewAlerts.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/event/NewAlerts.java
@@ -24,9 +24,9 @@ import org.sonar.api.notifications.*;
import java.util.Collection;
import java.util.Map;
-import org.sonar.core.notification.NotificationDispatcher;
-import org.sonar.core.notification.NotificationDispatcherMetadata;
-import org.sonar.core.notification.NotificationManager;
+import org.sonar.server.notification.NotificationDispatcher;
+import org.sonar.server.notification.NotificationDispatcherMetadata;
+import org.sonar.server.notification.NotificationManager;
/**
* This dispatcher means: "notify me each new alert event".
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java
index 29f84cafcca..53c4539b16f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java
@@ -44,7 +44,7 @@ import org.sonar.api.utils.log.Loggers;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.issue.db.IssueDto;
import org.sonar.core.issue.db.IssueStorage;
-import org.sonar.core.notification.NotificationManager;
+import org.sonar.server.notification.NotificationManager;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis;
import org.sonar.server.db.DbClient;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java
index b407ec4b635..0f342ce585a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java
@@ -26,7 +26,7 @@ import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.issue.internal.IssueChangeContext;
-import org.sonar.core.notification.NotificationManager;
+import org.sonar.server.notification.NotificationManager;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import org.sonar.api.rules.Rule;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/ChangesOnMyIssueNotificationDispatcher.java b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/ChangesOnMyIssueNotificationDispatcher.java
index b38eb2f855b..5aa290eb83b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/ChangesOnMyIssueNotificationDispatcher.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/ChangesOnMyIssueNotificationDispatcher.java
@@ -25,9 +25,9 @@ import org.sonar.api.notifications.*;
import javax.annotation.Nullable;
import java.util.Collection;
-import org.sonar.core.notification.NotificationDispatcher;
-import org.sonar.core.notification.NotificationDispatcherMetadata;
-import org.sonar.core.notification.NotificationManager;
+import org.sonar.server.notification.NotificationDispatcher;
+import org.sonar.server.notification.NotificationDispatcherMetadata;
+import org.sonar.server.notification.NotificationManager;
/**
* This dispatcher means: "notify me when a change is done on an issue that is assigned to me or reported by me".
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/DoNotFixNotificationDispatcher.java b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/DoNotFixNotificationDispatcher.java
index e4f0a5f81d9..1ee261ea214 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/DoNotFixNotificationDispatcher.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/DoNotFixNotificationDispatcher.java
@@ -27,9 +27,9 @@ import org.sonar.api.notifications.*;
import java.util.Collection;
import java.util.Map;
-import org.sonar.core.notification.NotificationDispatcher;
-import org.sonar.core.notification.NotificationDispatcherMetadata;
-import org.sonar.core.notification.NotificationManager;
+import org.sonar.server.notification.NotificationDispatcher;
+import org.sonar.server.notification.NotificationDispatcherMetadata;
+import org.sonar.server.notification.NotificationManager;
/**
* This dispatcher means: "notify me when an issue is resolved as false positive or won't fix".
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesNotificationDispatcher.java b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesNotificationDispatcher.java
index 3810279554f..c4be5a62776 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesNotificationDispatcher.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesNotificationDispatcher.java
@@ -24,9 +24,9 @@ import com.google.common.collect.Multimap;
import org.sonar.api.notifications.*;
import java.util.Collection;
-import org.sonar.core.notification.NotificationDispatcher;
-import org.sonar.core.notification.NotificationDispatcherMetadata;
-import org.sonar.core.notification.NotificationManager;
+import org.sonar.server.notification.NotificationDispatcher;
+import org.sonar.server.notification.NotificationDispatcherMetadata;
+import org.sonar.server.notification.NotificationManager;
/**
* This dispatcher means: "notify me when new issues are introduced during project analysis"
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcher.java b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcher.java
index e346b1af744..6c28a8aadf9 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcher.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcher.java
@@ -24,9 +24,9 @@ import org.sonar.api.notifications.*;
import java.util.Collection;
import java.util.Map;
-import org.sonar.core.notification.NotificationDispatcher;
-import org.sonar.core.notification.NotificationDispatcherMetadata;
-import org.sonar.core.notification.NotificationManager;
+import org.sonar.server.notification.NotificationDispatcher;
+import org.sonar.server.notification.NotificationDispatcherMetadata;
+import org.sonar.server.notification.NotificationManager;
/**
* This dispatcher means: "notify me when new issues are introduced during project analysis"
diff --git a/server/sonar-server/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java b/server/sonar-server/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java
new file mode 100644
index 00000000000..82706d1001d
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java
@@ -0,0 +1,177 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.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 java.io.IOException;
+import java.io.InvalidClassException;
+import java.util.Arrays;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.api.utils.SonarException;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.notification.db.NotificationQueueDao;
+import org.sonar.core.notification.db.NotificationQueueDto;
+import org.sonar.core.properties.PropertiesDao;
+
+public class DefaultNotificationManager implements NotificationManager {
+
+ private static final Logger LOG = Loggers.get(DefaultNotificationManager.class);
+
+ private static final String UNABLE_TO_READ_NOTIFICATION = "Unable to read notification";
+
+ private NotificationChannel[] notificationChannels;
+ private NotificationQueueDao notificationQueueDao;
+ private PropertiesDao propertiesDao;
+
+ private boolean alreadyLoggedDeserializationIssue = false;
+
+ /**
+ * Default constructor used by Pico
+ */
+ public DefaultNotificationManager(NotificationChannel[] channels, NotificationQueueDao notificationQueueDao, PropertiesDao propertiesDao) {
+ this.notificationChannels = channels;
+ this.notificationQueueDao = notificationQueueDao;
+ this.propertiesDao = propertiesDao;
+ }
+
+ /**
+ * Constructor if no notification channel
+ */
+ public DefaultNotificationManager(NotificationQueueDao notificationQueueDao, PropertiesDao propertiesDao) {
+ this(new NotificationChannel[0], notificationQueueDao, propertiesDao);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void scheduleForSending(Notification notification) {
+ NotificationQueueDto dto = NotificationQueueDto.toNotificationQueueDto(notification);
+ notificationQueueDao.insert(Arrays.asList(dto));
+ }
+
+ @Override
+ 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);
+ }
+ }));
+ }
+
+ /**
+ * Give the notification queue so that it can be processed
+ */
+ public Notification getFromQueue() {
+ int batchSize = 1;
+ List<NotificationQueueDto> notificationDtos = notificationQueueDao.findOldest(batchSize);
+ if (notificationDtos.isEmpty()) {
+ return null;
+ }
+ notificationQueueDao.delete(notificationDtos);
+
+ return convertToNotification(notificationDtos);
+ }
+
+ private Notification convertToNotification(List<NotificationQueueDto> notifications) {
+ try {
+ // If batchSize is increased then we should return a list instead of a single element
+ return notifications.get(0).toNotification();
+ } catch (InvalidClassException e) {
+ // SONAR-4739
+ if (!alreadyLoggedDeserializationIssue) {
+ logDeserializationIssue();
+ alreadyLoggedDeserializationIssue = true;
+ }
+ return null;
+ } catch (IOException e) {
+ throw new SonarException(UNABLE_TO_READ_NOTIFICATION, e);
+
+ } catch (ClassNotFoundException e) {
+ throw new SonarException(UNABLE_TO_READ_NOTIFICATION, e);
+ }
+ }
+
+ @VisibleForTesting
+ void logDeserializationIssue() {
+ LOG.warn("It is impossible to send pending notifications which existed prior to the upgrade of SonarQube. They will be ignored.");
+ }
+
+ public long count() {
+ return notificationQueueDao.count();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Multimap<String, NotificationChannel> findSubscribedRecipientsForDispatcher(NotificationDispatcher dispatcher, @Nullable Integer resourceId) {
+ String dispatcherKey = dispatcher.getKey();
+
+ SetMultimap<String, NotificationChannel> recipients = HashMultimap.create();
+ for (NotificationChannel channel : notificationChannels) {
+ String channelKey = channel.getKey();
+
+ // Find users subscribed globally to the dispatcher (i.e. not on a specific project)
+ addUsersToRecipientListForChannel(propertiesDao.findUsersForNotification(dispatcherKey, channelKey, null), recipients, channel);
+
+ if (resourceId != null) {
+ // Find users subscribed to the dispatcher specifically for the resource
+ addUsersToRecipientListForChannel(propertiesDao.findUsersForNotification(dispatcherKey, channelKey, resourceId.longValue()), recipients, channel);
+ }
+ }
+
+ return recipients;
+ }
+
+ @Override
+ public Multimap<String, NotificationChannel> findNotificationSubscribers(NotificationDispatcher dispatcher, @Nullable String componentKey) {
+ String dispatcherKey = dispatcher.getKey();
+
+ SetMultimap<String, NotificationChannel> recipients = HashMultimap.create();
+ for (NotificationChannel channel : notificationChannels) {
+ addUsersToRecipientListForChannel(propertiesDao.findNotificationSubscribers(dispatcherKey, channel.getKey(), componentKey), recipients, channel);
+ }
+
+ return recipients;
+ }
+
+ @VisibleForTesting
+ protected List<NotificationChannel> getChannels() {
+ return Arrays.asList(notificationChannels);
+ }
+
+ private static void addUsersToRecipientListForChannel(List<String> users, SetMultimap<String, NotificationChannel> recipients, NotificationChannel channel) {
+ for (String username : users) {
+ recipients.put(username, channel);
+ }
+ }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/notifications/NotificationCenter.java b/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationCenter.java
index cf43dca0c31..b739a60edcd 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/notifications/NotificationCenter.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationCenter.java
@@ -17,12 +17,11 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.notifications;
+package org.sonar.server.notification;
import com.google.common.collect.Lists;
import org.sonar.api.server.ServerSide;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.core.notification.NotificationDispatcherMetadata;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationDispatcher.java b/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationDispatcher.java
new file mode 100644
index 00000000000..3c99cbe0003
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationDispatcher.java
@@ -0,0 +1,130 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.notification;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.ExtensionPoint;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.api.server.ServerSide;
+
+/**
+ * <p>
+ * Plugins should extend this class to provide logic to determine which users are interested in receiving notifications,
+ * along with which delivery channels they selected.
+ * </p>
+ * For example:
+ * <ul>
+ * <li>notify me by email when someone comments an issue reported by me</li>
+ * <li>notify me by twitter when someone comments an issue assigned to me</li>
+ * <li>notify me by Jabber when someone mentions me in an issue comment</li>
+ * <li>send me by SMS when there are system notifications (like password reset, account creation, ...)</li>
+ * </ul>
+ *
+ * @since 2.10
+ */
+@ServerSide
+@ExtensionPoint
+public abstract class NotificationDispatcher {
+
+ private final String notificationType;
+
+ /**
+ * Additional information related to the notification, which will be used
+ * to know who should receive the notification.
+ */
+ public interface Context {
+ /**
+ * This method is not used any longer. Calling it will result in an {@link UnsupportedOperationException}.
+ *
+ * @deprecated Use {@link #addUser(String, NotificationChannel)} instead.
+ */
+ @Deprecated
+ void addUser(String userLogin);
+
+ /**
+ * Adds a user that will be notified through the given notification channel.
+ *
+ * @param userLogin the user login
+ * @param notificationChannel the notification channel to use for this user
+ */
+ void addUser(String userLogin, NotificationChannel notificationChannel);
+ }
+
+ /**
+ * Creates a new dispatcher for notifications of the given type.
+ *
+ * @param notificationType the type of notifications handled by this dispatcher
+ */
+ public NotificationDispatcher(String notificationType) {
+ this.notificationType = notificationType;
+ }
+
+ /**
+ * Creates a new generic dispatcher, used for any kind of notification.
+ * <p/>
+ * Should be avoided and replaced by the other constructor - as it is easier to understand that a
+ * dispatcher listens for a specific type of notification.
+ */
+ public NotificationDispatcher() {
+ this("");
+ }
+
+ /**
+ * The unique key of this dispatcher. By default it's the class name without the package prefix.
+ * <p/>
+ * The related label in l10n bundles is 'notification.dispatcher.<key>', for example 'notification.dispatcher.NewFalsePositive'.
+ */
+ public String getKey() {
+ return getClass().getSimpleName();
+ }
+
+ /**
+ * @since 5.1
+ */
+ public String getType() {
+ return notificationType;
+ }
+
+ /**
+ * <p>
+ * Performs the dispatch.
+ * </p>
+ */
+ public final void performDispatch(Notification notification, Context context) {
+ if (StringUtils.equals(notification.getType(), notificationType) || StringUtils.equals("", notificationType)) {
+ dispatch(notification, context);
+ }
+ }
+
+ /**
+ * <p>
+ * Implements the logic that defines which users will receive the notification.
+ * </p>
+ * The purpose of this method is to populate the context object with users, based on the type of notification and the content of the notification.
+ */
+ public abstract void dispatch(Notification notification, Context context);
+
+ @Override
+ public String toString() {
+ return getKey();
+ }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationDispatcherMetadata.java b/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationDispatcherMetadata.java
new file mode 100644
index 00000000000..de80ec7a2a7
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationDispatcherMetadata.java
@@ -0,0 +1,102 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.notification;
+
+import com.google.common.collect.Maps;
+import java.util.Map;
+import org.sonar.api.server.ServerSide;
+
+/**
+ * <p>
+ * Notification dispatchers (see {@link NotificationDispatcher}) can define their own metadata class in order
+ * to tell more about them.
+ * <p/>
+ * Instances of these classes must be declared in {@link org.sonar.api.SonarPlugin#getExtensions()}.
+ *
+ * @since 3.5
+ */
+@ServerSide
+public final class NotificationDispatcherMetadata {
+
+ public static final String GLOBAL_NOTIFICATION = "globalNotification";
+ public static final String PER_PROJECT_NOTIFICATION = "perProjectNotification";
+
+ private String dispatcherKey;
+ private Map<String, String> properties;
+
+ private NotificationDispatcherMetadata(String dispatcherKey) {
+ this.dispatcherKey = dispatcherKey;
+ this.properties = Maps.newHashMap();
+ }
+
+ /**
+ * Creates a new metadata instance for the given dispatcher.
+ * <p/>
+ * By default the key is the class name without package. It can be changed by overriding
+ * {@link NotificationDispatcher#getKey()}.
+ */
+ public static NotificationDispatcherMetadata create(String dispatcherKey) {
+ return new NotificationDispatcherMetadata(dispatcherKey);
+ }
+
+ /**
+ * Sets a property on this metadata object.
+ */
+ public NotificationDispatcherMetadata setProperty(String key, String value) {
+ properties.put(key, value);
+ return this;
+ }
+
+ /**
+ * Gives the property for the given key.
+ */
+ public String getProperty(String key) {
+ return properties.get(key);
+ }
+
+ /**
+ * Returns the unique key of the dispatcher.
+ */
+ public String getDispatcherKey() {
+ return dispatcherKey;
+ }
+
+ @Override
+ public String toString() {
+ return dispatcherKey;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ NotificationDispatcherMetadata that = (NotificationDispatcherMetadata) o;
+ return dispatcherKey.equals(that.dispatcherKey);
+ }
+
+ @Override
+ public int hashCode() {
+ return dispatcherKey.hashCode();
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationManager.java b/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationManager.java
new file mode 100644
index 00000000000..d499a8fcc5c
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationManager.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.notification;
+
+import com.google.common.collect.Multimap;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+
+/**
+ * <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
+ * the {@link NotificationManager#scheduleForSending(Notification)} method.
+ * </p>
+ */
+public interface NotificationManager {
+
+ /**
+ * 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 3.7.1
+ */
+ 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
+ * 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
+ */
+ Multimap<String, NotificationChannel> findSubscribedRecipientsForDispatcher(NotificationDispatcher dispatcher, @Nullable Integer resourceId);
+
+ Multimap<String, NotificationChannel> findNotificationSubscribers(NotificationDispatcher dispatcher, @Nullable String componentKey);
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java b/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationService.java
index e33af89334d..b3951e8ee02 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationService.java
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.notifications;
+package org.sonar.server.notification;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashMultimap;
@@ -38,10 +38,8 @@ import org.sonar.api.server.ServerSide;
import org.sonar.api.config.Settings;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.core.notification.NotificationDispatcher;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
-import org.sonar.core.notification.DefaultNotificationManager;
import org.sonar.jpa.session.DatabaseSessionFactory;
import org.sonar.server.db.DbClient;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/notifications/email/AlertsEmailTemplate.java b/server/sonar-server/src/main/java/org/sonar/server/notification/email/AlertsEmailTemplate.java
index 0ea9c3c5544..77a53501929 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/notifications/email/AlertsEmailTemplate.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/notification/email/AlertsEmailTemplate.java
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.notifications.email;
+package org.sonar.server.notification.email;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.config.EmailSettings;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java b/server/sonar-server/src/main/java/org/sonar/server/notification/email/EmailNotificationChannel.java
index 3b96270736b..de7dc0f81ea 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/notification/email/EmailNotificationChannel.java
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.notifications.email;
+package org.sonar.server.notification.email;
import java.net.MalformedURLException;
import java.net.URL;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/notifications/email/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/notification/email/package-info.java
index a81d297b0c7..f583fa78310 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/notifications/email/package-info.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/notification/email/package-info.java
@@ -18,6 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
@ParametersAreNonnullByDefault
-package org.sonar.server.notifications.email;
+package org.sonar.server.notification.email;
import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/notifications/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/notification/package-info.java
index 34262049a99..71e0748a18c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/notifications/package-info.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/notification/package-info.java
@@ -19,6 +19,6 @@
*/
@ParametersAreNonnullByDefault
-package org.sonar.server.notifications;
+package org.sonar.server.notification;
import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
index ca0260e5dea..21f69f49d65 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
@@ -38,7 +38,7 @@ import org.sonar.core.issue.IssueUpdater;
import org.sonar.core.issue.workflow.FunctionExecutor;
import org.sonar.core.issue.workflow.IssueWorkflow;
import org.sonar.core.metric.DefaultMetricFinder;
-import org.sonar.core.notification.DefaultNotificationManager;
+import org.sonar.server.notification.DefaultNotificationManager;
import org.sonar.core.permission.PermissionFacade;
import org.sonar.core.qualitygate.db.ProjectQgateAssociationDao;
import org.sonar.core.qualitygate.db.QualityGateConditionDao;
@@ -171,10 +171,10 @@ import org.sonar.server.measure.ws.ManualMeasuresWs;
import org.sonar.server.measure.ws.TimeMachineWs;
import org.sonar.server.metric.CoreCustomMetrics;
import org.sonar.server.metric.ws.MetricsWsModule;
-import org.sonar.server.notifications.NotificationCenter;
-import org.sonar.server.notifications.NotificationService;
-import org.sonar.server.notifications.email.AlertsEmailTemplate;
-import org.sonar.server.notifications.email.EmailNotificationChannel;
+import org.sonar.server.notification.NotificationCenter;
+import org.sonar.server.notification.NotificationService;
+import org.sonar.server.notification.email.AlertsEmailTemplate;
+import org.sonar.server.notification.email.EmailNotificationChannel;
import org.sonar.server.permission.InternalPermissionService;
import org.sonar.server.permission.InternalPermissionTemplateService;
import org.sonar.server.permission.PermissionFinder;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java
index 7fb81786e6c..a66e2c4085e 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java
@@ -40,7 +40,7 @@ import org.sonar.server.computation.issue.RuleCache;
import org.sonar.server.issue.notification.IssueChangeNotification;
import org.sonar.server.issue.notification.NewIssuesNotification;
import org.sonar.server.issue.notification.NewIssuesNotificationFactory;
-import org.sonar.server.notifications.NotificationService;
+import org.sonar.server.notification.NotificationService;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/event/NewAlertsTest.java b/server/sonar-server/src/test/java/org/sonar/server/event/NewAlertsTest.java
index 49bb6f7e6f0..d2b2f653eb9 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/event/NewAlertsTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/event/NewAlertsTest.java
@@ -24,8 +24,8 @@ import com.google.common.collect.Multimap;
import org.junit.Test;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.core.notification.NotificationDispatcher;
-import org.sonar.core.notification.NotificationManager;
+import org.sonar.server.notification.NotificationDispatcher;
+import org.sonar.server.notification.NotificationManager;
import static org.mockito.Mockito.*;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/ChangesOnMyIssueNotificationDispatcherTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/ChangesOnMyIssueNotificationDispatcherTest.java
index fdcefadb1ba..a11fd69465c 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/ChangesOnMyIssueNotificationDispatcherTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/ChangesOnMyIssueNotificationDispatcherTest.java
@@ -27,9 +27,9 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.notifications.*;
-import org.sonar.core.notification.NotificationDispatcher;
-import org.sonar.core.notification.NotificationDispatcherMetadata;
-import org.sonar.core.notification.NotificationManager;
+import org.sonar.server.notification.NotificationDispatcher;
+import org.sonar.server.notification.NotificationDispatcherMetadata;
+import org.sonar.server.notification.NotificationManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/DoNotFixNotificationDispatcherTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/DoNotFixNotificationDispatcherTest.java
index 4f42809dfb7..91061b4cb7e 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/DoNotFixNotificationDispatcherTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/DoNotFixNotificationDispatcherTest.java
@@ -25,9 +25,9 @@ import org.junit.Test;
import org.sonar.api.issue.Issue;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.core.notification.NotificationDispatcher;
-import org.sonar.core.notification.NotificationDispatcherMetadata;
-import org.sonar.core.notification.NotificationManager;
+import org.sonar.server.notification.NotificationDispatcher;
+import org.sonar.server.notification.NotificationDispatcherMetadata;
+import org.sonar.server.notification.NotificationManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/MyNewIssuesNotificationDispatcherTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/MyNewIssuesNotificationDispatcherTest.java
index 9173ef7227c..b697cdfe159 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/MyNewIssuesNotificationDispatcherTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/MyNewIssuesNotificationDispatcherTest.java
@@ -26,8 +26,8 @@ import org.junit.Before;
import org.junit.Test;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.core.notification.NotificationDispatcher;
-import org.sonar.core.notification.NotificationManager;
+import org.sonar.server.notification.NotificationDispatcher;
+import org.sonar.server.notification.NotificationManager;
import static org.mockito.Mockito.*;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcherTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcherTest.java
index 656d3bb4231..ec975b52966 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcherTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcherTest.java
@@ -25,8 +25,8 @@ import org.junit.Before;
import org.junit.Test;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.core.notification.NotificationDispatcher;
-import org.sonar.core.notification.NotificationManager;
+import org.sonar.server.notification.NotificationDispatcher;
+import org.sonar.server.notification.NotificationManager;
import static org.mockito.Mockito.*;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/notification/DefaultNotificationManagerTest.java b/server/sonar-server/src/test/java/org/sonar/server/notification/DefaultNotificationManagerTest.java
new file mode 100644
index 00000000000..e534646911c
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/notification/DefaultNotificationManagerTest.java
@@ -0,0 +1,180 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.notification;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.core.notification.db.NotificationQueueDao;
+import org.sonar.core.notification.db.NotificationQueueDto;
+import org.sonar.core.properties.PropertiesDao;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.io.InvalidClassException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.only;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class DefaultNotificationManagerTest extends AbstractDbUnitTestCase {
+
+ private DefaultNotificationManager manager;
+
+ @Mock
+ private PropertiesDao propertiesDao;
+
+ @Mock
+ private NotificationDispatcher dispatcher;
+
+ @Mock
+ private NotificationChannel emailChannel;
+
+ @Mock
+ private NotificationChannel twitterChannel;
+
+ @Mock
+ private NotificationQueueDao notificationQueueDao;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(dispatcher.getKey()).thenReturn("NewViolations");
+ when(emailChannel.getKey()).thenReturn("Email");
+ when(twitterChannel.getKey()).thenReturn("Twitter");
+
+ manager = new DefaultNotificationManager(new NotificationChannel[] {emailChannel, twitterChannel}, notificationQueueDao, propertiesDao);
+ }
+
+ @Test
+ public void shouldProvideChannelList() {
+ assertThat(manager.getChannels()).containsOnly(emailChannel, twitterChannel);
+
+ manager = new DefaultNotificationManager(notificationQueueDao, propertiesDao);
+ assertThat(manager.getChannels()).hasSize(0);
+ }
+
+ @Test
+ public void shouldPersist() {
+ Notification notification = new Notification("test");
+ manager.scheduleForSending(notification);
+
+ verify(notificationQueueDao, only()).insert(any(List.class));
+ }
+
+ @Test
+ public void shouldGetFromQueueAndDelete() {
+ Notification notification = new Notification("test");
+ NotificationQueueDto dto = NotificationQueueDto.toNotificationQueueDto(notification);
+ List<NotificationQueueDto> dtos = Arrays.asList(dto);
+ when(notificationQueueDao.findOldest(1)).thenReturn(dtos);
+
+ assertThat(manager.getFromQueue()).isNotNull();
+
+ InOrder inOrder = inOrder(notificationQueueDao);
+ inOrder.verify(notificationQueueDao).findOldest(1);
+ inOrder.verify(notificationQueueDao).delete(dtos);
+ }
+
+ // SONAR-4739
+ @Test
+ public void shouldNotFailWhenUnableToDeserialize() throws Exception {
+ NotificationQueueDto dto1 = mock(NotificationQueueDto.class);
+ when(dto1.toNotification()).thenThrow(new InvalidClassException("Pouet"));
+ List<NotificationQueueDto> dtos = Arrays.asList(dto1);
+ when(notificationQueueDao.findOldest(1)).thenReturn(dtos);
+
+ manager = spy(manager);
+ assertThat(manager.getFromQueue()).isNull();
+ assertThat(manager.getFromQueue()).isNull();
+
+ verify(manager, times(1)).logDeserializationIssue();
+ }
+
+ @Test
+ public void shouldFindNoRecipient() {
+ assertThat(manager.findSubscribedRecipientsForDispatcher(dispatcher, 45).asMap().entrySet()).hasSize(0);
+ }
+
+ @Test
+ public void shouldFindSubscribedRecipientForGivenResource() {
+ when(propertiesDao.findUsersForNotification("NewViolations", "Email", 45L)).thenReturn(Lists.newArrayList("user1", "user2"));
+ when(propertiesDao.findUsersForNotification("NewViolations", "Email", null)).thenReturn(Lists.newArrayList("user1", "user3"));
+ when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", 56L)).thenReturn(Lists.newArrayList("user2"));
+ when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", null)).thenReturn(Lists.newArrayList("user3"));
+ when(propertiesDao.findUsersForNotification("NewAlerts", "Twitter", null)).thenReturn(Lists.newArrayList("user4"));
+
+ Multimap<String, NotificationChannel> multiMap = manager.findSubscribedRecipientsForDispatcher(dispatcher, 45);
+ assertThat(multiMap.entries()).hasSize(4);
+
+ Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
+ assertThat(map.get("user1")).containsOnly(emailChannel);
+ assertThat(map.get("user2")).containsOnly(emailChannel);
+ assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel);
+ assertThat(map.get("user4")).isNull();
+ }
+
+ @Test
+ public void shouldFindSubscribedRecipientForNoResource() {
+ when(propertiesDao.findUsersForNotification("NewViolations", "Email", 45L)).thenReturn(Lists.newArrayList("user1", "user2"));
+ when(propertiesDao.findUsersForNotification("NewViolations", "Email", null)).thenReturn(Lists.newArrayList("user1", "user3"));
+ when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", 56L)).thenReturn(Lists.newArrayList("user2"));
+ when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", null)).thenReturn(Lists.newArrayList("user3"));
+ when(propertiesDao.findUsersForNotification("NewAlerts", "Twitter", null)).thenReturn(Lists.newArrayList("user4"));
+
+ Multimap<String, NotificationChannel> multiMap = manager.findSubscribedRecipientsForDispatcher(dispatcher, (Integer) null);
+ assertThat(multiMap.entries()).hasSize(3);
+
+ Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
+ assertThat(map.get("user1")).containsOnly(emailChannel);
+ assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel);
+ assertThat(map.get("user2")).isNull();
+ assertThat(map.get("user4")).isNull();
+ }
+
+ @Test
+ public void findNotificationSubscribers() {
+ when(propertiesDao.findNotificationSubscribers("NewViolations", "Email", "struts")).thenReturn(Lists.newArrayList("user1", "user2"));
+ when(propertiesDao.findNotificationSubscribers("NewViolations", "Twitter", "struts")).thenReturn(Lists.newArrayList("user2"));
+
+ Multimap<String, NotificationChannel> multiMap = manager.findNotificationSubscribers(dispatcher, "struts");
+ assertThat(multiMap.entries()).hasSize(3);
+
+ Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
+ assertThat(map.get("user1")).containsOnly(emailChannel);
+ assertThat(map.get("user2")).containsOnly(emailChannel, twitterChannel);
+ assertThat(map.get("other")).isNull();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/notifications/NotificationCenterTest.java b/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationCenterTest.java
index ecd6ef52117..e4d7b28fca5 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/notifications/NotificationCenterTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationCenterTest.java
@@ -17,14 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.notifications;
+package org.sonar.server.notification;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.core.notification.NotificationDispatcherMetadata;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationChannelTest.java b/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationChannelTest.java
new file mode 100644
index 00000000000..e0a0c823bcf
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationChannelTest.java
@@ -0,0 +1,44 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.notification;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+
+public class NotificationChannelTest {
+
+ @Test
+ public void defaultMethods() {
+ NotificationChannel channel = new FakeNotificationChannel();
+ assertThat(channel.getKey(), is("FakeNotificationChannel"));
+ assertThat(channel.toString(), is("FakeNotificationChannel"));
+ }
+
+ class FakeNotificationChannel extends NotificationChannel {
+ @Override
+ public void deliver(Notification notification, String username) {
+ }
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationDispatcherMetadataTest.java b/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationDispatcherMetadataTest.java
new file mode 100644
index 00000000000..7874ab50f45
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationDispatcherMetadataTest.java
@@ -0,0 +1,46 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.notification;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class NotificationDispatcherMetadataTest {
+
+ private NotificationDispatcherMetadata metadata;
+
+ @Before
+ public void init() {
+ metadata = NotificationDispatcherMetadata.create("NewViolations").setProperty("global", "true");
+ }
+
+ @Test
+ public void shouldReturnDispatcherKey() {
+ assertThat(metadata.getDispatcherKey()).isEqualTo("NewViolations");
+ }
+
+ @Test
+ public void shouldReturnProperty() {
+ assertThat(metadata.getProperty("global")).isEqualTo("true");
+ assertThat(metadata.getProperty("per-project")).isNull();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationDispatcherTest.java b/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationDispatcherTest.java
new file mode 100644
index 00000000000..e6ec1a7d450
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationDispatcherTest.java
@@ -0,0 +1,101 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.notification;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class NotificationDispatcherTest {
+
+ @Mock
+ private NotificationChannel channel;
+
+ @Mock
+ private Notification notification;
+
+ @Mock
+ private NotificationDispatcher.Context context;
+
+ @Before
+ public void init() {
+ MockitoAnnotations.initMocks(this);
+ when(notification.getType()).thenReturn("event1");
+ }
+
+ @Test
+ public void defaultMethods() {
+ NotificationDispatcher dispatcher = new FakeGenericNotificationDispatcher();
+ assertThat(dispatcher.getKey(), is("FakeGenericNotificationDispatcher"));
+ assertThat(dispatcher.toString(), is("FakeGenericNotificationDispatcher"));
+ }
+
+ @Test
+ public void shouldAlwaysRunDispatchForGenericDispatcher() {
+ NotificationDispatcher dispatcher = new FakeGenericNotificationDispatcher();
+ dispatcher.performDispatch(notification, context);
+
+ verify(context, times(1)).addUser("user1", channel);
+ }
+
+ @Test
+ public void shouldNotAlwaysRunDispatchForSpecificDispatcher() {
+ NotificationDispatcher dispatcher = new FakeSpecificNotificationDispatcher();
+
+ // a "event1" notif is sent
+ dispatcher.performDispatch(notification, context);
+ verify(context, never()).addUser("user1", channel);
+
+ // now, a "specific-event" notif is sent
+ when(notification.getType()).thenReturn("specific-event");
+ dispatcher.performDispatch(notification, context);
+ verify(context, times(1)).addUser("user1", channel);
+ }
+
+ class FakeGenericNotificationDispatcher extends NotificationDispatcher {
+ @Override
+ public void dispatch(Notification notification, Context context) {
+ context.addUser("user1", channel);
+ }
+ }
+
+ class FakeSpecificNotificationDispatcher extends NotificationDispatcher {
+
+ public FakeSpecificNotificationDispatcher() {
+ super("specific-event");
+ }
+
+ @Override
+ public void dispatch(Notification notification, Context context) {
+ context.addUser("user1", channel);
+ }
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationServiceTest.java
index c818dee6eac..515c4279864 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationServiceTest.java
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.notifications;
+package org.sonar.server.notification;
import com.google.common.collect.Sets;
import org.junit.Test;
@@ -26,8 +26,6 @@ import org.mockito.stubbing.Answer;
import org.sonar.api.config.Settings;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.core.notification.NotificationDispatcher;
-import org.sonar.core.notification.DefaultNotificationManager;
import org.sonar.core.properties.PropertiesDao;
import org.sonar.jpa.session.DatabaseSessionFactory;
import org.sonar.server.db.DbClient;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/notifications/NotificationTest.java b/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationTest.java
index 2c7c9194f9c..d90b000212c 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/notifications/NotificationTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/notification/NotificationTest.java
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.notifications;
+package org.sonar.server.notification;
import org.junit.Before;
import org.junit.Test;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/notifications/email/AlertsEmailTemplateTest.java b/server/sonar-server/src/test/java/org/sonar/server/notification/email/AlertsEmailTemplateTest.java
index 3834df8b9e8..3d81a495628 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/notifications/email/AlertsEmailTemplateTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/notification/email/AlertsEmailTemplateTest.java
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.notifications.email;
+package org.sonar.server.notification.email;
import org.junit.Before;
import org.junit.Test;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java b/server/sonar-server/src/test/java/org/sonar/server/notification/email/EmailNotificationChannelTest.java
index 3c5e94587af..e7cbcce0d57 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/notification/email/EmailNotificationChannelTest.java
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.notifications.email;
+package org.sonar.server.notification.email;
import java.io.IOException;
import java.net.ServerSocket;