Currently it's only removed from API. Will be removed from batch after that events are in Compute Engine (for change of quality gate status)
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;
/**
* This dispatcher means: "notify me each new alert event".
import org.junit.Test;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.notifications.NotificationDispatcher;
-import org.sonar.api.notifications.NotificationManager;
+import org.sonar.core.notification.NotificationDispatcher;
+import org.sonar.core.notification.NotificationManager;
import static org.mockito.Mockito.*;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.issue.internal.IssueChangeContext;
-import org.sonar.api.notifications.NotificationManager;
+import org.sonar.core.notification.NotificationManager;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.Rule;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.issue.internal.IssueChangeContext;
-import org.sonar.api.notifications.NotificationManager;
+import org.sonar.core.notification.NotificationManager;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import org.sonar.api.rules.Rule;
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;
/**
* This dispatcher means: "notify me when a change is done on an issue that is assigned to me or reported by me".
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;
/**
* This dispatcher means: "notify me when an issue is resolved as false positive or won't fix".
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;
/**
* This dispatcher means: "notify me when new issues are introduced during project analysis"
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;
/**
* This dispatcher means: "notify me when new issues are introduced during project analysis"
import com.google.common.collect.Lists;
import org.sonar.api.server.ServerSide;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.notifications.NotificationDispatcherMetadata;
+import org.sonar.core.notification.NotificationDispatcherMetadata;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.api.config.Settings;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.notifications.NotificationDispatcher;
+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.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 static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
import org.sonar.api.issue.Issue;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.notifications.NotificationDispatcher;
-import org.sonar.api.notifications.NotificationDispatcherMetadata;
-import org.sonar.api.notifications.NotificationManager;
+import org.sonar.core.notification.NotificationDispatcher;
+import org.sonar.core.notification.NotificationDispatcherMetadata;
+import org.sonar.core.notification.NotificationManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
+++ /dev/null
-/*
- * 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.
- */
-///*
-// * 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.issue.notification;
-//
-//import org.junit.Before;
-//import org.junit.Test;
-//import org.junit.runner.RunWith;
-//import org.mockito.Mock;
-//import org.mockito.Mockito;
-//import org.mockito.runners.MockitoJUnitRunner;
-//import org.sonar.api.issue.internal.DefaultIssue;
-//import org.sonar.api.issue.internal.IssueChangeContext;
-//import org.sonar.api.notifications.Notification;
-//import org.sonar.api.notifications.NotificationManager;
-//import org.sonar.api.resources.File;
-//import org.sonar.api.resources.Project;
-//import org.sonar.core.component.ResourceComponent;
-//import org.sonar.server.notifications.NotificationService;
-//
-//import java.util.Date;
-//
-//import static org.assertj.core.api.Assertions.assertThat;
-//import static org.mockito.Mockito.mock;
-//
-//@RunWith(MockitoJUnitRunner.class)
-//public class IssueNotificationsTest {
-//
-// @Mock
-// NotificationManager manager;
-//
-// IssueNotifications issueNotifications;
-//
-// @Before
-// public void setUp() throws Exception {
-// issueNotifications = new IssueNotifications(manager, mock(NotificationService.class));
-// }
-//
-// // @Test
-// // public void should_send_new_issues() throws Exception {
-// // Date date = DateUtils.parseDateTime("2013-05-18T13:00:03+0200");
-// // Project project = new Project("struts").setAnalysisDate(date);
-// // IssuesBySeverity issuesBySeverity = mock(IssuesBySeverity.class);
-// // when(issuesBySeverity.size()).thenReturn(42);
-// // when(issuesBySeverity.issues("MINOR")).thenReturn(10);
-// // Notification notification = issueNotifications.sendNewIssues(project, issuesBySeverity);
-// //
-// // assertThat(notification.getFieldValue("count")).isEqualTo("42");
-// // assertThat(notification.getFieldValue("count-MINOR")).isEqualTo("10");
-// // assertThat(DateUtils.parseDateTime(notification.getFieldValue("projectDate"))).isEqualTo(date);
-// // Mockito.verify(manager).scheduleForSending(notification);
-// // }
-//
-// @Test
-// public void should_send_changes() throws Exception {
-// IssueChangeContext context = IssueChangeContext.createScan(new Date());
-// DefaultIssue issue = new DefaultIssue()
-// .setMessage("the message")
-// .setKey("ABCDE")
-// .setAssignee("freddy")
-// .setFieldChange(context, "resolution", null, "FIXED")
-// .setFieldChange(context, "status", "OPEN", "RESOLVED")
-// .setFieldChange(context, "assignee", "simon", null)
-// .setSendNotifications(true)
-// .setComponentKey("struts:Action")
-// .setProjectKey("struts");
-//
-// Notification notification = issueNotifications.sendChanges(issue, "charlie", null, new Project("struts"), null, null, false);
-//
-// assertThat(notification.getFieldValue("message")).isEqualTo("the message");
-// assertThat(notification.getFieldValue("key")).isEqualTo("ABCDE");
-// assertThat(notification.getFieldValue("componentKey")).isEqualTo("struts:Action");
-// assertThat(notification.getFieldValue("componentName")).isNull();
-// assertThat(notification.getFieldValue("old.resolution")).isNull();
-// assertThat(notification.getFieldValue("new.resolution")).isEqualTo("FIXED");
-// assertThat(notification.getFieldValue("old.status")).isEqualTo("OPEN");
-// 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);
-// }
-//
-// @Test
-// public void should_send_changes_with_comment() throws Exception {
-// DefaultIssue issue = new DefaultIssue()
-// .setMessage("the message")
-// .setKey("ABCDE")
-// .setAssignee("freddy")
-// .setComponentKey("struts:Action")
-// .setProjectKey("struts");
-// Notification notification = issueNotifications.sendChanges(issue, "charlie", null, new Project("struts"), null, "I don't know how to fix it?", false);
-//
-// assertThat(notification.getFieldValue("message")).isEqualTo("the message");
-// assertThat(notification.getFieldValue("key")).isEqualTo("ABCDE");
-// assertThat(notification.getFieldValue("comment")).isEqualTo("I don't know how to fix it?");
-// Mockito.verify(manager).scheduleForSending(notification);
-// }
-//
-// @Test
-// public void should_send_changes_with_component_name() throws Exception {
-// IssueChangeContext context = IssueChangeContext.createScan(new Date());
-// DefaultIssue issue = new DefaultIssue()
-// .setMessage("the message")
-// .setKey("ABCDE")
-// .setAssignee("freddy")
-// .setFieldChange(context, "resolution", null, "FIXED")
-// .setSendNotifications(true)
-// .setComponentKey("struts:Action.java")
-// .setProjectKey("struts");
-// Notification notification = issueNotifications.sendChanges(issue, "charlie", null, new Project("struts"),
-// new ResourceComponent(File.create("Action.java", "Action.java", null, false).setEffectiveKey("struts:Action.java")), null, false);
-//
-// assertThat(notification.getFieldValue("message")).isEqualTo("the message");
-// assertThat(notification.getFieldValue("key")).isEqualTo("ABCDE");
-// assertThat(notification.getFieldValue("componentKey")).isEqualTo("struts:Action.java");
-// assertThat(notification.getFieldValue("componentName")).isEqualTo("Action.java");
-// assertThat(notification.getFieldValue("old.resolution")).isNull();
-// assertThat(notification.getFieldValue("new.resolution")).isEqualTo("FIXED");
-// Mockito.verify(manager).scheduleForSending(notification);
-// }
-//
-// @Test
-// public void should_not_send_changes_if_no_diffs() throws Exception {
-// DefaultIssue issue = new DefaultIssue()
-// .setMessage("the message")
-// .setKey("ABCDE")
-// .setComponentKey("struts:Action")
-// .setProjectKey("struts");
-// issueNotifications.sendChanges(issue, "charlie", null, new Project("struts"), null, null, false);
-//
-// Mockito.verifyZeroInteractions(manager);
-// }
-//}
-
import org.junit.Test;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.notifications.NotificationDispatcher;
-import org.sonar.api.notifications.NotificationManager;
+import org.sonar.core.notification.NotificationDispatcher;
+import org.sonar.core.notification.NotificationManager;
import static org.mockito.Mockito.*;
import org.junit.Test;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.notifications.NotificationDispatcher;
-import org.sonar.api.notifications.NotificationManager;
+import org.sonar.core.notification.NotificationDispatcher;
+import org.sonar.core.notification.NotificationManager;
import static org.mockito.Mockito.*;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.notifications.NotificationDispatcherMetadata;
+import org.sonar.core.notification.NotificationDispatcherMetadata;
import static org.assertj.core.api.Assertions.assertThat;
import org.sonar.api.config.Settings;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.notifications.NotificationDispatcher;
+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.api.measures.Metric;
import org.sonar.api.measures.Metric.Level;
import org.sonar.api.notifications.Notification;
-import org.sonar.api.notifications.NotificationManager;
+import org.sonar.core.notification.NotificationManager;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;
private void checkQualityGateStatusChange(Resource resource, DecoratorContext context, Measure currentStatus, Measure pastStatus) {
String alertText = currentStatus.getAlertText();
Level alertLevel = currentStatus.getDataAsLevel();
- String alertName = null;
+ String alertName;
boolean isNewAlert = true;
if (pastStatus != null && pastStatus.getDataAsLevel() != alertLevel) {
// The alert status has changed
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.Metric;
import org.sonar.api.notifications.Notification;
-import org.sonar.api.notifications.NotificationManager;
+import org.sonar.core.notification.NotificationManager;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.batch.RequiresDB;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.notifications.NotificationDispatcher;
-import org.sonar.api.notifications.NotificationManager;
import org.sonar.api.utils.SonarException;
import org.sonar.core.notification.db.NotificationQueueDao;
import org.sonar.core.notification.db.NotificationQueueDto;
--- /dev/null
+/*
+ * 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.core.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();
+ }
+
+}
--- /dev/null
+/*
+ * 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.core.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();
+ }
+}
--- /dev/null
+/*
+ * 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.core.notification;
+
+import com.google.common.collect.Multimap;
+import org.sonar.api.batch.BatchSide;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.api.server.ServerSide;
+import org.sonar.api.batch.InstantiationStrategy;
+
+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
+ * the {@link NotificationManager#scheduleForSending(Notification)} method.
+ * </p>
+ *
+ * @since 2.10
+ */
+@ServerSide
+@BatchSide
+@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
+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);
+}
import org.mockito.MockitoAnnotations;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.notifications.NotificationDispatcher;
import org.sonar.core.notification.db.NotificationQueueDao;
import org.sonar.core.notification.db.NotificationQueueDto;
import org.sonar.core.properties.PropertiesDao;
--- /dev/null
+/*
+ * 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.core.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) {
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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.core.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();
+ }
+}
--- /dev/null
+/*
+ * 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.core.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);
+ }
+ }
+
+}
import java.util.Map;
/**
- * <p>
* This class represents a notification that will be delivered to users. This is a general concept and it has no
- * knowledge of the possible ways to be delivered (see {@link NotificationChannel}) or of the users who should
- * receive it (see {@link NotificationDispatcher}).
- * </p>
+ * knowledge of the possible ways to be delivered (see {@link NotificationChannel}).
* <p>
* When creating a new notification, it is strongly advised to give a default message that can be used by channels
* that don't want to specifically format messages for different notification types. You can use
+++ /dev/null
-/*
- * 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.api.notifications;
-
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.ExtensionPoint;
-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();
- }
-
-}
+++ /dev/null
-/*
- * 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.api.notifications;
-
-import com.google.common.collect.Maps;
-import org.sonar.api.ExtensionPoint;
-import org.sonar.api.server.ServerSide;
-
-import java.util.Map;
-
-/**
- * <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
-@ExtensionPoint
-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 org.sonar.api.notifications.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();
- }
-}
+++ /dev/null
-/*
- * 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.api.notifications;
-
-import com.google.common.collect.Multimap;
-import org.sonar.api.batch.BatchSide;
-import org.sonar.api.server.ServerSide;
-import org.sonar.api.batch.InstantiationStrategy;
-
-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
- * the {@link NotificationManager#scheduleForSending(Notification)} method.
- * </p>
- *
- * @since 2.10
- */
-@ServerSide
-@BatchSide
-@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
-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);
-}
+++ /dev/null
-/*
- * 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.api.notifications;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-import org.junit.Test;
-
-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) {
- }
- }
-
-}
+++ /dev/null
-/*
- * 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.api.notifications;
-
-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();
- }
-}
+++ /dev/null
-/*
- * 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.api.notifications;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-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);
- }
- }
-
-}