diff options
author | Evgeny Mandrikov <mandrikov@gmail.com> | 2011-07-19 13:09:51 +0400 |
---|---|---|
committer | Evgeny Mandrikov <mandrikov@gmail.com> | 2011-07-20 19:02:49 +0400 |
commit | 5f730dc9ed7a80ad2de100940e051279c2018e1d (patch) | |
tree | 1fa0100c6f94fd93d5921a0722ce01dfd3481a96 /sonar-server | |
parent | a77a5b874799dc15edffe740a3ca34d896813238 (diff) | |
download | sonarqube-5f730dc9ed7a80ad2de100940e051279c2018e1d.tar.gz sonarqube-5f730dc9ed7a80ad2de100940e051279c2018e1d.zip |
SONAR-2596,SONAR-2600 Improve notification mechanism
* Persist notifications into DB for later delivery.
* Add sonar-email-plugin, which sends notifications by email.
Diffstat (limited to 'sonar-server')
25 files changed, 289 insertions, 1392 deletions
diff --git a/sonar-server/pom.xml b/sonar-server/pom.xml index df611dabce4..6f6f74371af 100644 --- a/sonar-server/pom.xml +++ b/sonar-server/pom.xml @@ -155,11 +155,6 @@ <groupId>org.mortbay.jetty</groupId> <artifactId>jetty-util</artifactId> </dependency> - <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-email</artifactId> - <version>1.2</version> - </dependency> <!-- unit tests --> <dependency> @@ -167,12 +162,6 @@ <artifactId>sonar-testing-harness</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>dumbster</groupId> - <artifactId>dumbster</artifactId> - <version>1.6</version> - <scope>test</scope> - </dependency> </dependencies> @@ -465,6 +454,12 @@ <version>${project.version}</version> <scope>provided</scope> </dependency> + <dependency> + <groupId>org.codehaus.sonar.plugins</groupId> + <artifactId>sonar-email-plugin</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> </dependencies> </profile> diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/Notification.java b/sonar-server/src/main/java/org/sonar/server/notifications/Notification.java deleted file mode 100644 index 395f90af602..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/notifications/Notification.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications; - -/** - * Marker interface for data model objects that represents notifications. - * - * @since 2.10 - */ -public interface Notification { -} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationChannel.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationChannel.java deleted file mode 100644 index 1b2726768b1..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationChannel.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications; - -import org.sonar.api.ServerExtension; - -import java.io.Serializable; - -/** - * Provides logic to deliver notification. - * For example: - * <ul> - * <li>email - sends email as soon as possible</li> - * <li>email (digest) - collects notifications and sends them together once a day</li> - * <li>gtalk - sends a chat message as soon as possible</li> - * </ul> - * - * @since 2.10 - */ -public abstract class NotificationChannel implements ServerExtension { - - /** - * @return unique key of this channel - */ - public String getKey() { - return getClass().getSimpleName(); - } - - public abstract Serializable createDataForPersistance(Notification notification, Integer userId); - - public abstract void deliver(Serializable notificationData); - -} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationDispatcher.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationDispatcher.java deleted file mode 100644 index a82ee0004b1..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationDispatcher.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications; - -import org.sonar.api.ServerExtension; - -/** - * Provides logic to determine which users are interested in receiving notification. - * Has no knowledge about the way of delivery. - * For example: - * <ul> - * <li>notify me when someone comments on review created by me</li> - * <li>notify me when someone comments on review assigned to me</li> - * <li>notify me when someone mentions me in comment for review</li> - * <li>send me system notifications (like password reset, account creation, ...)</li> - * </ul> - * - * @since 2.10 - */ -public abstract class NotificationDispatcher implements ServerExtension { - - public interface Context { - void addUser(Integer userId); - } - - /** - * @return unique key of this dispatcher - */ - public String getKey() { - return getClass().getSimpleName(); - } - - /** - * @return recipients - */ - public abstract void dispatch(Notification notification, Context context); - -} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationManager.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationManager.java deleted file mode 100644 index 304f3c664b8..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationManager.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.SetMultimap; -import com.google.common.collect.Sets; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.ServerComponent; -import org.sonar.api.utils.TimeProfiler; - -import java.io.Serializable; -import java.util.Map; -import java.util.Set; - -/** - * @since 2.10 - */ -public class NotificationManager implements ServerComponent { // TODO should be available on batch side too - - private static final Logger LOG = LoggerFactory.getLogger(NotificationManager.class); - private static final TimeProfiler TIME_PROFILER = new TimeProfiler().setLogger(LOG); - - private NotificationQueue queue; - private NotificationDispatcher[] dispatchers; - private NotificationChannel[] channels; - - public NotificationManager(NotificationQueue queue, NotificationDispatcher[] dispatchers, NotificationChannel[] channels) { - this.queue = queue; - this.dispatchers = dispatchers; - this.channels = channels; - } - - public void scheduleForSending(Notification notification) { - TIME_PROFILER.start("Scheduling " + notification); - SetMultimap<Integer, NotificationChannel> recipients = HashMultimap.create(); - for (NotificationChannel channel : channels) { - for (NotificationDispatcher dispatcher : dispatchers) { - final Set<Integer> possibleRecipients = Sets.newHashSet(); - NotificationDispatcher.Context context = new NotificationDispatcher.Context() { - public void addUser(Integer userId) { - possibleRecipients.add(userId); - } - }; - dispatcher.dispatch(notification, context); - for (Integer userId : possibleRecipients) { - if (isEnabled(userId, channel, dispatcher)) { - recipients.put(userId, channel); - } - } - } - } - for (Map.Entry<Integer, NotificationChannel> entry : recipients.entries()) { - Integer userId = entry.getKey(); - NotificationChannel channel = entry.getValue(); - LOG.info("For user {} via {}", userId, channel.getKey()); - Serializable notificationData = channel.createDataForPersistance(notification, userId); - queue.add(notificationData, channel); - } - TIME_PROFILER.stop(); - } - - boolean isEnabled(Integer userId, NotificationChannel channel, NotificationDispatcher dispatcher) { - return true; // FIXME for the moment we will accept everything - } - -} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationQueue.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationQueue.java deleted file mode 100644 index bf02333d9a7..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationQueue.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications; - -import org.sonar.api.ServerComponent; - -import java.io.Serializable; -import java.util.concurrent.ConcurrentLinkedQueue; - -/** - * @since 2.10 - */ -public class NotificationQueue implements ServerComponent { - - private ConcurrentLinkedQueue<Element> queue = new ConcurrentLinkedQueue<Element>(); - - public static class Element { - String channelKey; - Serializable notificationData; - - public Element(String channelKey, Serializable notificationData) { - this.channelKey = channelKey; - this.notificationData = notificationData; - } - } - - public Element get() { - return queue.poll(); - } - - public void add(Serializable notificationData, NotificationChannel channel) { - queue.add(new Element(channel.getKey(), notificationData)); - } - -} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java index 6faab4f133f..b77fe0e6296 100644 --- a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java +++ b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java @@ -19,13 +19,21 @@ */ package org.sonar.server.notifications; -import com.google.common.collect.Maps; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.SetMultimap; +import com.google.common.collect.Sets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.ServerComponent; -import org.sonar.server.notifications.NotificationQueue.Element; +import org.sonar.api.notifications.Notification; +import org.sonar.api.notifications.NotificationChannel; +import org.sonar.api.notifications.NotificationDispatcher; +import org.sonar.api.utils.TimeProfiler; +import org.sonar.core.notifications.DefaultNotificationManager; +import org.sonar.jpa.entity.NotificationQueueElement; import java.util.Map; +import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -35,27 +43,28 @@ import java.util.concurrent.TimeUnit; */ public class NotificationService implements ServerComponent { - private static Logger LOG = LoggerFactory.getLogger(NotificationService.class); + private static final Logger LOG = LoggerFactory.getLogger(NotificationService.class); + private static final TimeProfiler TIME_PROFILER = new TimeProfiler(LOG); private ScheduledExecutorService executorService; private long period = 10; // FIXME small value just for tests - private NotificationQueue queue; - private Map<String, NotificationChannel> channels = Maps.newHashMap(); + private DefaultNotificationManager manager; + private NotificationChannel[] channels; + private NotificationDispatcher[] dispatchers; /** * Default constructor when no channels. */ - public NotificationService(NotificationQueue queue) { - this(queue, new NotificationChannel[0]); + public NotificationService(DefaultNotificationManager manager, NotificationDispatcher[] dispatchers) { + this(manager, dispatchers, new NotificationChannel[0]); LOG.warn("There is no channels - all notifications would be ignored!"); } - public NotificationService(NotificationQueue queue, NotificationChannel[] channels) { - this.queue = queue; - for (NotificationChannel channel : channels) { - this.channels.put(channel.getKey(), channel); - } + public NotificationService(DefaultNotificationManager manager, NotificationDispatcher[] dispatchers, NotificationChannel[] channels) { + this.manager = manager; + this.channels = channels; + this.dispatchers = dispatchers; } /** @@ -89,21 +98,48 @@ public class NotificationService implements ServerComponent { * Visibility has been relaxed for tests. */ void processQueue() { - NotificationQueue.Element element = queue.get(); - while (element != null) { - deliver(element); - element = queue.get(); + NotificationQueueElement queueElement = manager.getFromQueue(); + while (queueElement != null) { + deliver(queueElement.getNotification()); + queueElement = manager.getFromQueue(); } } /** * Visibility has been relaxed for tests. */ - void deliver(Element element) { - NotificationChannel channel = channels.get(element.channelKey); - if (channel != null) { - channel.deliver(element.notificationData); + void deliver(Notification notification) { + TIME_PROFILER.start("Delivering notification " + notification); + SetMultimap<String, NotificationChannel> recipients = HashMultimap.create(); + for (NotificationChannel channel : channels) { + for (NotificationDispatcher dispatcher : dispatchers) { + final Set<String> possibleRecipients = Sets.newHashSet(); + NotificationDispatcher.Context context = new NotificationDispatcher.Context() { + public void addUser(String username) { + if (username != null) { + possibleRecipients.add(username); + } + } + }; + dispatcher.dispatch(notification, context); + for (String username : possibleRecipients) { + if (isEnabled(username, channel, dispatcher)) { + recipients.put(username, channel); + } + } + } } + for (Map.Entry<String, NotificationChannel> entry : recipients.entries()) { + String username = entry.getKey(); + NotificationChannel channel = entry.getValue(); + LOG.info("For user {} via {}", username, channel); + channel.deliver(notification, username); + } + TIME_PROFILER.stop(); + } + + boolean isEnabled(String username, NotificationChannel channel, NotificationDispatcher dispatcher) { + return true; // FIXME for the moment we will accept everything } } diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailConfiguration.java b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailConfiguration.java deleted file mode 100644 index e16e536cd0c..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailConfiguration.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications.email; - -import org.apache.commons.configuration.Configuration; -import org.sonar.api.ServerComponent; - -/** - * Ruby uses constants from this class. - * - * @since 2.10 - */ -public class EmailConfiguration implements ServerComponent { - - public static final String SMTP_HOST = "email.smtp_host"; - public static final String SMTP_HOST_DEFAULT = ""; - public static final String SMTP_PORT = "email.smtp_port"; - public static final String SMTP_PORT_DEFAULT = "25"; - public static final String SMTP_USE_TLS = "email.smtp_use_tls"; - public static final boolean SMTP_USE_TLS_DEFAULT = false; - public static final String SMTP_USERNAME = "email.smtp_username"; - public static final String SMTP_USERNAME_DEFAULT = ""; - public static final String SMTP_PASSWORD = "email.smtp_password"; - public static final String SMTP_PASSWORD_DEFAULT = ""; - public static final String FROM = "email.from"; - public static final String FROM_DEFAULT = "noreply@nowhere"; - public static final String PREFIX = "email.prefix"; - public static final String PREFIX_DEFAULT = "[SONAR]"; - - private Configuration configuration; - - public EmailConfiguration(Configuration configuration) { - this.configuration = configuration; - } - - public String getSmtpHost() { - return configuration.getString(SMTP_HOST, SMTP_HOST_DEFAULT); - } - - public String getSmtpPort() { - return configuration.getString(SMTP_PORT, SMTP_PORT_DEFAULT); - } - - public boolean isUseTLS() { - return configuration.getBoolean(SMTP_USE_TLS, SMTP_USE_TLS_DEFAULT); - } - - public String getSmtpUsername() { - return configuration.getString(SMTP_USERNAME, SMTP_USERNAME_DEFAULT); - } - - public String getSmtpPassword() { - return configuration.getString(SMTP_PASSWORD, SMTP_PASSWORD_DEFAULT); - } - - public String getFrom() { - return configuration.getString(FROM, FROM_DEFAULT); - } - - public String getPrefix() { - return configuration.getString(PREFIX, PREFIX_DEFAULT); - } - -} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java deleted file mode 100644 index c2227e6506b..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications.email; - -import org.apache.commons.lang.builder.ToStringBuilder; - -import java.io.Serializable; - -/** - * @since 2.10 - */ -public class EmailMessage implements Serializable { - - private String from; - private String to; - private String subject; - private String message; - private String messageId; - - /** - * @param from full name of user, who initiated this message or null, if message was initiated by Sonar - */ - public EmailMessage setFrom(String from) { - this.from = from; - return this; - } - - /** - * @see #setFrom(String) - */ - public String getFrom() { - return from; - } - - /** - * @param to email address where to send this message - */ - public EmailMessage setTo(String to) { - this.to = to; - return this; - } - - /** - * @see #setTo(String) - */ - public String getTo() { - return to; - } - - /** - * @param subject message subject - */ - public EmailMessage setSubject(String subject) { - this.subject = subject; - return this; - } - - /** - * @see #setSubject(String) - */ - public String getSubject() { - return subject; - } - - /** - * @param message message body - */ - public EmailMessage setMessage(String message) { - this.message = message; - return this; - } - - /** - * @see #setMessage(String) - */ - public String getMessage() { - return message; - } - - /** - * @param messageId id of message for threading - */ - public EmailMessage setMessageId(String messageId) { - this.messageId = messageId; - return this; - } - - /** - * @see #setMessageId(String) - */ - public String getMessageId() { - return messageId; - } - - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this).toString(); - } - -} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessageTemplate.java b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessageTemplate.java deleted file mode 100644 index ed465c724cd..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessageTemplate.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications.email; - -import org.sonar.api.ServerExtension; -import org.sonar.server.notifications.Notification; - -/** - * @since 2.10 - */ -public abstract class EmailMessageTemplate implements ServerExtension { - - public abstract EmailMessage format(Notification notification); - -} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java deleted file mode 100644 index 7c07f56f063..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications.email; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.mail.Email; -import org.apache.commons.mail.EmailException; -import org.apache.commons.mail.SimpleEmail; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.database.model.User; -import org.sonar.jpa.session.DatabaseSessionFactory; -import org.sonar.server.notifications.Notification; -import org.sonar.server.notifications.NotificationChannel; - -import java.io.Serializable; - -/** - * References: - * <ul> - * <li><a href="http://tools.ietf.org/html/rfc4021">Registration of Mail and MIME Header Fields</a></li> - * <li><a href="http://tools.ietf.org/html/rfc2919">List-Id: A Structured Field and Namespace for the Identification of Mailing Lists</a></li> - * <li><a href="https://github.com/blog/798-threaded-email-notifications">GitHub: Threaded Email Notifications</a></li> - * </ul> - * - * @since 2.10 - */ -public class EmailNotificationChannel extends NotificationChannel { - - private static final Logger LOG = LoggerFactory.getLogger(EmailNotificationChannel.class); - - /** - * @see Email#setSocketConnectionTimeout(int) - * @see Email#setSocketTimeout(int) - */ - private static final int SOCKET_TIMEOUT = 30000; - - private static final String FROM_NAME_DEFAULT = "Sonar"; - private static final String SUBJECT_DEFAULT = "Notification"; - - private EmailConfiguration configuration; - private EmailMessageTemplate[] templates; - private DatabaseSessionFactory sessionFactory; - - public EmailNotificationChannel(EmailConfiguration configuration, EmailMessageTemplate[] templates, DatabaseSessionFactory sessionFactory) { - this.configuration = configuration; - this.templates = templates; - this.sessionFactory = sessionFactory; - } - - private User getUserById(Integer id) { - return sessionFactory.getSession().getEntity(User.class, id); - } - - @Override - public Serializable createDataForPersistance(Notification notification, Integer userId) { - for (EmailMessageTemplate template : templates) { - EmailMessage email = template.format(notification); - if (email != null) { - User user = getUserById(userId); - email.setTo(user.getEmail()); - return email; - } - } - return null; - } - - @Override - public void deliver(Serializable notificationData) { - if (StringUtils.isBlank(configuration.getSmtpHost())) { - LOG.warn("SMTP host was not configured - email will not be sent"); - return; - } - try { - send((EmailMessage) notificationData); - } catch (EmailException e) { - LOG.error("Unable to send email", e); - } - } - - private void send(EmailMessage emailMessage) throws EmailException { - LOG.info("Sending email: {}", emailMessage); - // TODO - String domain = "nemo.sonarsource.org"; - String listId = "<sonar." + domain + ">"; - String serverUrl = "http://nemo.sonarsource.org"; - - SimpleEmail email = new SimpleEmail(); - /* - * Set headers for proper threading: - * GMail will not group messages, even if they have same subject, but don't have "In-Reply-To" and "References" headers. - * TODO investigate threading in other clients like KMail, Thunderbird, Outlook - */ - if (StringUtils.isNotEmpty(emailMessage.getMessageId())) { - String messageId = "<" + emailMessage.getMessageId() + "@" + domain + ">"; - email.addHeader("In-Reply-To", messageId); - email.addHeader("References", messageId); - } - // Set headers for proper filtering - email.addHeader("List-Id", listId); - email.addHeader("List-Archive", serverUrl); - // Set general information - email.setFrom(configuration.getFrom(), StringUtils.defaultIfBlank(emailMessage.getFrom(), FROM_NAME_DEFAULT)); - email.addTo(emailMessage.getTo(), " "); - String subject = StringUtils.defaultIfBlank(StringUtils.trimToEmpty(configuration.getPrefix()) + " ", "") - + StringUtils.defaultString(emailMessage.getSubject(), SUBJECT_DEFAULT); - email.setSubject(subject); - email.setMsg(emailMessage.getMessage()); - // Send - email.setHostName(configuration.getSmtpHost()); - email.setSmtpPort(Integer.parseInt(configuration.getSmtpPort())); - email.setTLS(configuration.isUseTLS()); - if (StringUtils.isNotBlank(configuration.getSmtpUsername()) || StringUtils.isNotBlank(configuration.getSmtpPassword())) { - email.setAuthentication(configuration.getSmtpUsername(), configuration.getSmtpPassword()); - } - email.setSocketConnectionTimeout(SOCKET_TIMEOUT); - email.setSocketTimeout(SOCKET_TIMEOUT); - email.send(); - } - - /** - * Send test email. This method called from Ruby. - * - * @throws EmailException when unable to send - */ - public void sendTestEmail(String toAddress, String subject, String message) throws EmailException { - EmailMessage emailMessage = new EmailMessage(); - emailMessage.setTo(toAddress); - emailMessage.setSubject(subject); - emailMessage.setMessage(message); - send(emailMessage); - } - -} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMe.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMe.java deleted file mode 100644 index 51af872a075..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMe.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications.reviews; - -import org.sonar.jpa.entity.Review; -import org.sonar.server.notifications.Notification; -import org.sonar.server.notifications.NotificationDispatcher; - -/** - * This dispatcher means: "notify me when when someone comments on review assigned to me". - * - * @since 2.10 - */ -public class CommentOnReviewAssignedToMe extends NotificationDispatcher { - - @Override - public void dispatch(Notification notification, Context context) { - if (notification instanceof CommentOnReviewNotification) { - Review review = ((CommentOnReviewNotification) notification).getReview(); - if (review.getAssigneeId() != null) { - context.addUser(review.getAssigneeId()); - } - } - } - -} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMe.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMe.java deleted file mode 100644 index 868e6286108..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMe.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications.reviews; - -import org.sonar.jpa.entity.Review; -import org.sonar.server.notifications.Notification; -import org.sonar.server.notifications.NotificationDispatcher; - -/** - * This dispatcher means: "notify me when when someone comments on review created by me". - * - * @since 2.10 - */ -public class CommentOnReviewCreatedByMe extends NotificationDispatcher { - - @Override - public void dispatch(Notification notification, Context context) { - if (notification instanceof CommentOnReviewNotification) { - Review review = ((CommentOnReviewNotification) notification).getReview(); - context.addUser(review.getUserId()); - } - } - -} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java deleted file mode 100644 index 460db32c9a4..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications.reviews; - -import org.sonar.server.notifications.Notification; -import org.sonar.server.notifications.email.EmailMessage; -import org.sonar.server.notifications.email.EmailMessageTemplate; - -/** - * Email template for {@link CommentOnReviewNotification}. - */ -public class CommentOnReviewEmailTemplate extends EmailMessageTemplate { - - @Override - public EmailMessage format(Notification notification) { - if (notification instanceof CommentOnReviewNotification) { - CommentOnReviewNotification event = (CommentOnReviewNotification) notification; - EmailMessage email = new EmailMessage() - .setFrom(event.getAuthor().getName()) - .setMessageId("review/" + event.getReview().getId()) - .setSubject("Review #" + event.getReview().getId()) - .setMessage(event.getComment()); - return email; - } - return null; - } - -} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewNotification.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewNotification.java deleted file mode 100644 index 48a89b36c40..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewNotification.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications.reviews; - -import org.sonar.api.database.model.User; -import org.sonar.jpa.entity.Review; -import org.sonar.server.notifications.Notification; - -/** - * @since 2.10 - */ -public class CommentOnReviewNotification implements Notification { - - private Review review; - private User author; - private String comment; - - public CommentOnReviewNotification(Review review, User author, String comment) { - this.review = review; - this.author = author; - this.comment = comment; - } - - public Review getReview() { - return review; - } - - public User getAuthor() { - return author; - } - - public String getComment() { - return comment; - } - -} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/ReviewsNotificationManager.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/ReviewsNotificationManager.java index 602f4e51791..e8200bd4b06 100644 --- a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/ReviewsNotificationManager.java +++ b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/ReviewsNotificationManager.java @@ -20,10 +20,11 @@ package org.sonar.server.notifications.reviews; import org.sonar.api.ServerComponent; +import org.sonar.api.database.model.Review; import org.sonar.api.database.model.User; -import org.sonar.jpa.entity.Review; +import org.sonar.api.notifications.Notification; +import org.sonar.api.notifications.NotificationManager; import org.sonar.jpa.session.DatabaseSessionFactory; -import org.sonar.server.notifications.NotificationManager; /** * @since 2.10 @@ -55,7 +56,12 @@ public class ReviewsNotificationManager implements ServerComponent { public void notifyCommentAdded(Long reviewId, Integer userId, String comment) { Review review = getReviewById(reviewId); User author = getUserById(userId); - CommentOnReviewNotification notification = new CommentOnReviewNotification(review, author, comment); + + Notification notification = new Notification("review"); + notification // FIXME include info about review + .setFieldValue("author", author.getLogin()) + .setFieldValue("comment", comment); + notificationManager.scheduleForSending(notification); } diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index 49bcf2d6aa4..df234f0c490 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -40,6 +40,7 @@ import org.sonar.api.utils.TimeProfiler; import org.sonar.core.components.DefaultMetricFinder; import org.sonar.core.components.DefaultModelFinder; import org.sonar.core.components.DefaultRuleFinder; +import org.sonar.core.notifications.DefaultNotificationManager; import org.sonar.jpa.dao.DaoFacade; import org.sonar.jpa.dao.MeasuresDao; import org.sonar.jpa.dao.ProfilesDao; @@ -55,14 +56,7 @@ import org.sonar.server.database.EmbeddedDatabaseFactory; import org.sonar.server.database.JndiDatabaseConnector; import org.sonar.server.filters.FilterExecutor; import org.sonar.server.mavendeployer.MavenRepository; -import org.sonar.server.notifications.NotificationManager; -import org.sonar.server.notifications.NotificationQueue; import org.sonar.server.notifications.NotificationService; -import org.sonar.server.notifications.email.EmailConfiguration; -import org.sonar.server.notifications.email.EmailNotificationChannel; -import org.sonar.server.notifications.reviews.CommentOnReviewAssignedToMe; -import org.sonar.server.notifications.reviews.CommentOnReviewCreatedByMe; -import org.sonar.server.notifications.reviews.CommentOnReviewEmailTemplate; import org.sonar.server.notifications.reviews.ReviewsNotificationManager; import org.sonar.server.plugins.*; import org.sonar.server.qualitymodel.DefaultModelManager; @@ -193,16 +187,9 @@ public final class Platform { servicesContainer.as(Characteristics.CACHE).addComponent(JRubyI18n.class); // Notifications - servicesContainer.as(Characteristics.CACHE).addComponent(NotificationQueue.class); servicesContainer.as(Characteristics.CACHE).addComponent(NotificationService.class); - servicesContainer.as(Characteristics.CACHE).addComponent(NotificationManager.class); + servicesContainer.as(Characteristics.CACHE).addComponent(DefaultNotificationManager.class); servicesContainer.as(Characteristics.CACHE).addComponent(ReviewsNotificationManager.class); - // FIXME next five lines here just for tests: - servicesContainer.as(Characteristics.CACHE).addComponent(EmailConfiguration.class); - servicesContainer.as(Characteristics.CACHE).addComponent(EmailNotificationChannel.class); - servicesContainer.as(Characteristics.CACHE).addComponent(CommentOnReviewEmailTemplate.class); - servicesContainer.as(Characteristics.CACHE).addComponent(CommentOnReviewAssignedToMe.class); - servicesContainer.as(Characteristics.CACHE).addComponent(CommentOnReviewCreatedByMe.class); servicesContainer.start(); } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/email_configuration_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/email_configuration_controller.rb index 77c3172f3b2..163ece76c50 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/email_configuration_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/email_configuration_controller.rb @@ -22,26 +22,24 @@ class EmailConfigurationController < ApplicationController SECTION=Navigation::SECTION_CONFIGURATION before_filter :admin_required - EmailConfiguration = org.sonar.server.notifications.email.EmailConfiguration - def index - @smtp_host = Property.value(EmailConfiguration::SMTP_HOST, nil, EmailConfiguration::SMTP_HOST_DEFAULT) - @smtp_port = Property.value(EmailConfiguration::SMTP_PORT, nil, EmailConfiguration::SMTP_PORT_DEFAULT) - @smtp_use_tls = Property.value(EmailConfiguration::SMTP_USE_TLS, nil, EmailConfiguration::SMTP_USE_TLS_DEFAULT) == 'true' - @smtp_username = Property.value(EmailConfiguration::SMTP_USERNAME, nil, EmailConfiguration::SMTP_USERNAME_DEFAULT) - @smtp_password = Property.value(EmailConfiguration::SMTP_PASSWORD, nil, EmailConfiguration::SMTP_PASSWORD_DEFAULT) - @email_from = Property.value(EmailConfiguration::FROM, nil, EmailConfiguration::FROM_DEFAULT) - @email_prefix = Property.value(EmailConfiguration::PREFIX, nil, EmailConfiguration::PREFIX_DEFAULT) + @smtp_host = Property.value(configuration::SMTP_HOST, nil, configuration::SMTP_HOST_DEFAULT) + @smtp_port = Property.value(configuration::SMTP_PORT, nil, configuration::SMTP_PORT_DEFAULT) + @smtp_use_tls = Property.value(configuration::SMTP_USE_TLS, nil, configuration::SMTP_USE_TLS_DEFAULT) == 'true' + @smtp_username = Property.value(configuration::SMTP_USERNAME, nil, configuration::SMTP_USERNAME_DEFAULT) + @smtp_password = Property.value(configuration::SMTP_PASSWORD, nil, configuration::SMTP_PASSWORD_DEFAULT) + @email_from = Property.value(configuration::FROM, nil, configuration::FROM_DEFAULT) + @email_prefix = Property.value(configuration::PREFIX, nil, configuration::PREFIX_DEFAULT) end def save - Property.set(EmailConfiguration::SMTP_HOST, params[:smtp_host]) - Property.set(EmailConfiguration::SMTP_PORT, params[:smtp_port]) - Property.set(EmailConfiguration::SMTP_USE_TLS, params[:smtp_use_tls] == 'true') - Property.set(EmailConfiguration::SMTP_USERNAME, params[:smtp_username]) - Property.set(EmailConfiguration::SMTP_PASSWORD, params[:smtp_password]) - Property.set(EmailConfiguration::FROM, params[:email_from]) - Property.set(EmailConfiguration::PREFIX, params[:email_prefix]) + Property.set(configuration::SMTP_HOST, params[:smtp_host]) + Property.set(configuration::SMTP_PORT, params[:smtp_port]) + Property.set(configuration::SMTP_USE_TLS, params[:smtp_use_tls] == 'true') + Property.set(configuration::SMTP_USERNAME, params[:smtp_username]) + Property.set(configuration::SMTP_PASSWORD, params[:smtp_password]) + Property.set(configuration::FROM, params[:email_from]) + Property.set(configuration::PREFIX, params[:email_prefix]) redirect_to :action => 'index' end @@ -53,7 +51,7 @@ class EmailConfigurationController < ApplicationController flash[:notice] = 'You must provide address where to send test email' else begin - java_facade.getCoreComponentByClassname('org.sonar.server.notifications.email.EmailNotificationChannel').sendTestEmail(to_address, subject, message) + java_facade.getComponentByClassname('email', 'org.sonar.plugins.email.EmailNotificationChannel').sendTestEmail(to_address, subject, message) rescue Exception => e flash[:error] = e.message end @@ -61,4 +59,10 @@ class EmailConfigurationController < ApplicationController redirect_to :action => 'index' end + private + + def configuration + java_facade.getComponentByClassname('email', 'org.sonar.plugins.email.EmailConfiguration').class + end + end diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/215_create_notifications.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/215_create_notifications.rb new file mode 100644 index 00000000000..ed8196464e3 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/215_create_notifications.rb @@ -0,0 +1,33 @@ +# +# Sonar, entreprise quality control tool. +# Copyright (C) 2008-2011 SonarSource +# mailto:contact AT sonarsource DOT com +# +# Sonar 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. +# +# Sonar 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 Sonar; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 +# + +# +# Sonar 2.10 +# +class CreateNotifications < ActiveRecord::Migration + + def self.up + create_table 'notifications' do |t| + t.column 'created_at', :datetime, :null => true + t.column 'data', :binary, :null => true + end + end + +end diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/NotificationManagerTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/NotificationManagerTest.java deleted file mode 100644 index b1a00deb603..00000000000 --- a/sonar-server/src/test/java/org/sonar/server/notifications/NotificationManagerTest.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.io.Serializable; - -public class NotificationManagerTest { - - private static Integer USER_SIMON = 1; - private static Integer USER_EVGENY = 2; - - private NotificationChannel emailChannel; - private NotificationChannel gtalkChannel; - - private NotificationDispatcher commentOnReviewAssignedToMe; - private Integer assignee; - private NotificationDispatcher commentOnReviewCreatedByMe; - private Integer creator; - - private NotificationQueue queue; - private NotificationManager manager; - - @Before - public void setUp() { - emailChannel = mock(NotificationChannel.class); - when(emailChannel.getKey()).thenReturn("email"); - doAnswer(new Answer<Serializable>() { - public Serializable answer(InvocationOnMock invocation) throws Throwable { - return (Serializable) invocation.getArguments()[1]; - } - }).when(emailChannel).createDataForPersistance(any(Notification.class), any(Integer.class)); - - gtalkChannel = mock(NotificationChannel.class); - when(gtalkChannel.getKey()).thenReturn("gtalk"); - doAnswer(new Answer<Serializable>() { - public Serializable answer(InvocationOnMock invocation) throws Throwable { - return (Serializable) invocation.getArguments()[1]; - } - }).when(gtalkChannel).createDataForPersistance(any(Notification.class), any(Integer.class)); - - commentOnReviewAssignedToMe = mock(NotificationDispatcher.class); - when(commentOnReviewAssignedToMe.getKey()).thenReturn("comment on review assigned to me"); - doAnswer(new Answer<Object>() { - public Object answer(InvocationOnMock invocation) throws Throwable { - ((NotificationDispatcher.Context) invocation.getArguments()[1]).addUser(assignee); - return null; - } - }).when(commentOnReviewAssignedToMe).dispatch(any(Notification.class), any(NotificationDispatcher.Context.class)); - - commentOnReviewCreatedByMe = mock(NotificationDispatcher.class); - when(commentOnReviewCreatedByMe.getKey()).thenReturn("comment on review created by me"); - doAnswer(new Answer<Object>() { - public Object answer(InvocationOnMock invocation) throws Throwable { - ((NotificationDispatcher.Context) invocation.getArguments()[1]).addUser(creator); - return null; - } - }).when(commentOnReviewCreatedByMe).dispatch(any(Notification.class), any(NotificationDispatcher.Context.class)); - - NotificationDispatcher[] dispatchers = new NotificationDispatcher[] { commentOnReviewAssignedToMe, commentOnReviewCreatedByMe }; - NotificationChannel[] channels = new NotificationChannel[] { emailChannel, gtalkChannel }; - queue = mock(NotificationQueue.class); - manager = spy(new NotificationManager(queue, dispatchers, channels)); - doReturn(false).when(manager).isEnabled(any(Integer.class), any(NotificationChannel.class), any(NotificationDispatcher.class)); - } - - /** - * Given: - * Simon wants to receive notifications by email on comments for reviews assigned to him or created by him. - * - * When: - * Freddy adds comment to review created by Simon and assigned to Simon. - * - * Then: - * Only one notification should be delivered to Simon by Email. - */ - @Test - public void scenario1() { - doReturn(true).when(manager).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe); - doReturn(true).when(manager).isEnabled(USER_SIMON, emailChannel, commentOnReviewCreatedByMe); - - Notification notification = mock(Notification.class); - creator = USER_SIMON; - assignee = USER_SIMON; - - manager.scheduleForSending(notification); - - verify(queue).add(USER_SIMON, emailChannel); - verifyNoMoreInteractions(queue); - } - - /** - * Given: - * Evgeny wants to receive notification by GTalk on comments for reviews created by him. - * Simon wants to receive notification by Email on comments for reviews assigned to him. - * - * When: - * Freddy adds comment to review created by Evgeny and assigned to Simon. - * - * Then: - * Two notifications should be delivered - one to Simon by Email and another to Evgeny by GTalk. - */ - @Test - public void scenario2() { - doReturn(true).when(manager).isEnabled(USER_EVGENY, gtalkChannel, commentOnReviewCreatedByMe); - doReturn(true).when(manager).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe); - - Notification notification = mock(Notification.class); - creator = USER_EVGENY; - assignee = USER_SIMON; - - manager.scheduleForSending(notification); - - verify(queue).add(USER_EVGENY, gtalkChannel); - verify(queue).add(USER_SIMON, emailChannel); - verifyNoMoreInteractions(queue); - } - - /** - * Given: - * Simon wants to receive notifications by Email and GTLak on comments for reviews assigned to him. - * - * When: - * Freddy adds comment to review created by Evgeny and assigned to Simon. - * - * Then: - * Two notifications should be delivered to Simon - one by Email and another by GTalk. - */ - @Test - public void scenario3() { - doReturn(true).when(manager).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe); - doReturn(true).when(manager).isEnabled(USER_SIMON, gtalkChannel, commentOnReviewAssignedToMe); - - Notification notification = mock(Notification.class); - creator = USER_EVGENY; - assignee = USER_SIMON; - - manager.scheduleForSending(notification); - - verify(queue).add(USER_SIMON, gtalkChannel); - verify(queue).add(USER_SIMON, emailChannel); - verifyNoMoreInteractions(queue); - } - - /** - * Given: - * Nobody wants to receive notifications. - * - * When: - * Freddy adds comment to review created by Evgeny and assigned to Simon. - * - * Then: - * No notifications. - */ - @Test - public void scenario4() { - Notification notification = mock(Notification.class); - creator = USER_EVGENY; - assignee = USER_SIMON; - - manager.scheduleForSending(notification); - - verifyNoMoreInteractions(queue); - } - -} diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java index 7c1c0b03615..46633e27a90 100644 --- a/sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java @@ -19,33 +19,185 @@ */ package org.sonar.server.notifications; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.*; import org.junit.Before; import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.sonar.api.notifications.Notification; +import org.sonar.api.notifications.NotificationChannel; +import org.sonar.api.notifications.NotificationDispatcher; +import org.sonar.core.notifications.DefaultNotificationManager; +import org.sonar.jpa.entity.NotificationQueueElement; public class NotificationServiceTest { - private NotificationQueue queue; + private static String USER_SIMON = "simon"; + private static String USER_EVGENY = "evgeny"; + + private NotificationChannel emailChannel; + private NotificationChannel gtalkChannel; + + private NotificationDispatcher commentOnReviewAssignedToMe; + private String assignee; + private NotificationDispatcher commentOnReviewCreatedByMe; + private String creator; + + private DefaultNotificationManager manager; private NotificationService service; @Before public void setUp() { - queue = mock(NotificationQueue.class); - service = spy(new NotificationService(queue)); - service.setPeriod(10); + emailChannel = mock(NotificationChannel.class); + when(emailChannel.getKey()).thenReturn("email"); + + gtalkChannel = mock(NotificationChannel.class); + when(gtalkChannel.getKey()).thenReturn("gtalk"); + + commentOnReviewAssignedToMe = mock(NotificationDispatcher.class); + when(commentOnReviewAssignedToMe.getKey()).thenReturn("comment on review assigned to me"); + doAnswer(new Answer<Object>() { + public Object answer(InvocationOnMock invocation) throws Throwable { + ((NotificationDispatcher.Context) invocation.getArguments()[1]).addUser(assignee); + return null; + } + }).when(commentOnReviewAssignedToMe).dispatch(any(Notification.class), any(NotificationDispatcher.Context.class)); + + commentOnReviewCreatedByMe = mock(NotificationDispatcher.class); + when(commentOnReviewCreatedByMe.getKey()).thenReturn("comment on review created by me"); + doAnswer(new Answer<Object>() { + public Object answer(InvocationOnMock invocation) throws Throwable { + ((NotificationDispatcher.Context) invocation.getArguments()[1]).addUser(creator); + return null; + } + }).when(commentOnReviewCreatedByMe).dispatch(any(Notification.class), any(NotificationDispatcher.Context.class)); + + NotificationDispatcher[] dispatchers = new NotificationDispatcher[] { commentOnReviewAssignedToMe, commentOnReviewCreatedByMe }; + NotificationChannel[] channels = new NotificationChannel[] { emailChannel, gtalkChannel }; + manager = mock(DefaultNotificationManager.class); + service = spy(new NotificationService(manager, dispatchers, channels)); + doReturn(false).when(service).isEnabled(any(String.class), any(NotificationChannel.class), any(NotificationDispatcher.class)); } @Test public void shouldPeriodicallyProcessQueue() throws Exception { - service.start(); + NotificationQueueElement queueElement = mock(NotificationQueueElement.class); + Notification notification = mock(Notification.class); + when(queueElement.getNotification()).thenReturn(notification); + when(manager.getFromQueue()).thenReturn(queueElement).thenReturn(null); + doNothing().when(service).deliver(any(Notification.class)); - NotificationQueue.Element element = mock(NotificationQueue.Element.class); - when(queue.get()).thenReturn(element); + service.setPeriod(10); + service.start(); Thread.sleep(50); + service.stop(); - verify(service, atLeastOnce()).deliver(element); + verify(service).deliver(notification); + } - service.stop(); + /** + * Given: + * Simon wants to receive notifications by email on comments for reviews assigned to him or created by him. + * + * When: + * Freddy adds comment to review created by Simon and assigned to Simon. + * + * Then: + * Only one notification should be delivered to Simon by Email. + */ + @Test + public void scenario1() { + doReturn(true).when(service).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe); + doReturn(true).when(service).isEnabled(USER_SIMON, emailChannel, commentOnReviewCreatedByMe); + + Notification notification = mock(Notification.class); + creator = USER_SIMON; + assignee = USER_SIMON; + + service.deliver(notification); + + verify(emailChannel).deliver(notification, USER_SIMON); + verifyNoMoreInteractions(emailChannel); + verifyNoMoreInteractions(gtalkChannel); } + + /** + * Given: + * Evgeny wants to receive notification by GTalk on comments for reviews created by him. + * Simon wants to receive notification by Email on comments for reviews assigned to him. + * + * When: + * Freddy adds comment to review created by Evgeny and assigned to Simon. + * + * Then: + * Two notifications should be delivered - one to Simon by Email and another to Evgeny by GTalk. + */ + @Test + public void scenario2() { + doReturn(true).when(service).isEnabled(USER_EVGENY, gtalkChannel, commentOnReviewCreatedByMe); + doReturn(true).when(service).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe); + + Notification notification = mock(Notification.class); + creator = USER_EVGENY; + assignee = USER_SIMON; + + service.deliver(notification); + + verify(emailChannel).deliver(notification, USER_SIMON); + verify(gtalkChannel).deliver(notification, USER_EVGENY); + verifyNoMoreInteractions(emailChannel); + verifyNoMoreInteractions(gtalkChannel); + } + + /** + * Given: + * Simon wants to receive notifications by Email and GTLak on comments for reviews assigned to him. + * + * When: + * Freddy adds comment to review created by Evgeny and assigned to Simon. + * + * Then: + * Two notifications should be delivered to Simon - one by Email and another by GTalk. + */ + @Test + public void scenario3() { + doReturn(true).when(service).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe); + doReturn(true).when(service).isEnabled(USER_SIMON, gtalkChannel, commentOnReviewAssignedToMe); + + Notification notification = mock(Notification.class); + creator = USER_EVGENY; + assignee = USER_SIMON; + + service.deliver(notification); + + verify(emailChannel).deliver(notification, USER_SIMON); + verify(gtalkChannel).deliver(notification, USER_SIMON); + verifyNoMoreInteractions(emailChannel); + verifyNoMoreInteractions(gtalkChannel); + } + + /** + * Given: + * Nobody wants to receive notifications. + * + * When: + * Freddy adds comment to review created by Evgeny and assigned to Simon. + * + * Then: + * No notifications. + */ + @Test + public void scenario4() { + Notification notification = mock(Notification.class); + creator = USER_EVGENY; + assignee = USER_SIMON; + + service.deliver(notification); + + verifyNoMoreInteractions(emailChannel); + verifyNoMoreInteractions(gtalkChannel); + } + } diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java deleted file mode 100644 index 0b51f3e989b..00000000000 --- a/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications.email; - -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.dumbster.smtp.SimpleSmtpServer; -import com.dumbster.smtp.SmtpMessage; -import org.apache.commons.mail.EmailException; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import java.io.IOException; -import java.net.ServerSocket; - -public class EmailNotificationChannelTest { - - private static int port; - - private SimpleSmtpServer server; - - private EmailConfiguration configuration; - private EmailNotificationChannel channel; - - @BeforeClass - public static void selectPort() { - port = getNextAvailablePort(); - } - - private static int getNextAvailablePort() { - try { - ServerSocket socket = new ServerSocket(0); - int unusedPort = socket.getLocalPort(); - socket.close(); - return unusedPort; - } catch (IOException e) { - throw new RuntimeException("Error getting an available port from system", e); - } - } - - @Before - public void setUp() { - server = SimpleSmtpServer.start(port); - configuration = mock(EmailConfiguration.class); - channel = new EmailNotificationChannel(configuration, null, null); - } - - @After - public void tearDown() { - if (!server.isStopped()) { - server.stop(); - } - } - - @Test - public void shouldSendTestEmail() throws Exception { - configure(); - channel.sendTestEmail("user@nowhere", "Test Message from Sonar", "This is a test message from Sonar."); - - assertThat(server.getReceivedEmailSize(), is(1)); - SmtpMessage email = (SmtpMessage) server.getReceivedEmail().next(); - - assertThat(email.getHeaderValue("From"), is("Sonar <server@nowhere>")); - assertThat(email.getHeaderValue("To"), is("<user@nowhere>")); - assertThat(email.getHeaderValue("Subject"), is("[SONAR] Test Message from Sonar")); - assertThat(email.getBody(), is("This is a test message from Sonar.")); - } - - @Test(expected = EmailException.class) - public void shouldThrowAnExceptionWhenUnableToSendTestEmail() throws Exception { - configure(); - server.stop(); - - channel.sendTestEmail("user@nowhere", "Test Message from Sonar", "This is a test message from Sonar."); - } - - @Test - public void shouldNotSendEmailWhenHostnameNotConfigured() throws Exception { - EmailMessage emailMessage = new EmailMessage() - .setTo("user@nowhere") - .setSubject("Foo") - .setMessage("Bar"); - channel.deliver(emailMessage); - assertThat(server.getReceivedEmailSize(), is(0)); - } - - @Test - public void shouldSendThreadedEmail() throws Exception { - configure(); - EmailMessage emailMessage = new EmailMessage() - .setMessageId("reviews/view/1") - .setFrom("Full Username") - .setTo("user@nowhere") - .setSubject("Review #3") - .setMessage("I'll take care of this violation."); - channel.deliver(emailMessage); - - assertThat(server.getReceivedEmailSize(), is(1)); - SmtpMessage email = (SmtpMessage) server.getReceivedEmail().next(); - - assertThat(email.getHeaderValue("In-Reply-To"), is("<reviews/view/1@nemo.sonarsource.org>")); - assertThat(email.getHeaderValue("References"), is("<reviews/view/1@nemo.sonarsource.org>")); - - assertThat(email.getHeaderValue("List-Id"), is("<sonar.nemo.sonarsource.org>")); - assertThat(email.getHeaderValue("List-Archive"), is("http://nemo.sonarsource.org")); - - assertThat(email.getHeaderValue("From"), is("Full Username <server@nowhere>")); - assertThat(email.getHeaderValue("To"), is("<user@nowhere>")); - assertThat(email.getHeaderValue("Subject"), is("[SONAR] Review #3")); - assertThat(email.getBody(), is("I'll take care of this violation.")); - } - - @Test - public void shouldSendNonThreadedEmail() { - configure(); - EmailMessage emailMessage = new EmailMessage() - .setTo("user@nowhere") - .setSubject("Foo") - .setMessage("Bar"); - channel.deliver(emailMessage); - - assertThat(server.getReceivedEmailSize(), is(1)); - SmtpMessage email = (SmtpMessage) server.getReceivedEmail().next(); - - assertThat(email.getHeaderValue("In-Reply-To"), nullValue()); - assertThat(email.getHeaderValue("References"), nullValue()); - - assertThat(email.getHeaderValue("List-Id"), is("<sonar.nemo.sonarsource.org>")); - assertThat(email.getHeaderValue("List-Archive"), is("http://nemo.sonarsource.org")); - - assertThat(email.getHeaderValue("From"), is("Sonar <server@nowhere>")); - assertThat(email.getHeaderValue("To"), is("<user@nowhere>")); - assertThat(email.getHeaderValue("Subject"), is("[SONAR] Foo")); - assertThat(email.getBody(), is("Bar")); - } - - @Test - public void shouldNotThrowAnExceptionWhenUnableToSendEmail() { - configure(); - server.stop(); - - EmailMessage emailMessage = new EmailMessage() - .setTo("user@nowhere") - .setSubject("Foo") - .setMessage("Bar"); - channel.deliver(emailMessage); - } - - private void configure() { - when(configuration.getSmtpHost()).thenReturn("localhost"); - when(configuration.getSmtpPort()).thenReturn(Integer.toString(port)); - when(configuration.getFrom()).thenReturn("server@nowhere"); - when(configuration.getPrefix()).thenReturn("[SONAR]"); - } - -} diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMeTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMeTest.java deleted file mode 100644 index 62c375c500c..00000000000 --- a/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMeTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications.reviews; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.database.model.User; -import org.sonar.jpa.entity.Review; -import org.sonar.server.notifications.NotificationDispatcher; - -public class CommentOnReviewAssignedToMeTest { - - private NotificationDispatcher.Context context; - private CommentOnReviewAssignedToMe dispatcher; - - @Before - public void setUp() { - context = mock(NotificationDispatcher.Context.class); - dispatcher = new CommentOnReviewAssignedToMe(); - } - - @Test - public void shouldDispatchToAssignee() { - CommentOnReviewNotification notification = new CommentOnReviewNotification(new Review().setAssigneeId(1), new User(), "comment"); - dispatcher.dispatch(notification, context); - verify(context).addUser(1); - - notification = new CommentOnReviewNotification(new Review().setAssigneeId(2), new User(), "comment"); - dispatcher.dispatch(notification, context); - verify(context).addUser(2); - } - - @Test - public void shouldNotDispatchWhenNotAssigned() { - CommentOnReviewNotification notification = new CommentOnReviewNotification(new Review(), new User(), "comment"); - dispatcher.dispatch(notification, context); - verifyNoMoreInteractions(context); - } - -} diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMeTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMeTest.java deleted file mode 100644 index 4f1c1766153..00000000000 --- a/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMeTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar 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. - * - * Sonar 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 Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.notifications.reviews; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.database.model.User; -import org.sonar.jpa.entity.Review; -import org.sonar.server.notifications.NotificationDispatcher; - -public class CommentOnReviewCreatedByMeTest { - - private NotificationDispatcher.Context context; - private CommentOnReviewCreatedByMe dispatcher; - - @Before - public void setUp() { - context = mock(NotificationDispatcher.Context.class); - dispatcher = new CommentOnReviewCreatedByMe(); - } - - @Test - public void shouldDispatchToCreator() { - CommentOnReviewNotification notification = new CommentOnReviewNotification(new Review().setUserId(1), new User(), "comment"); - dispatcher.dispatch(notification, context); - verify(context).addUser(1); - - notification = new CommentOnReviewNotification(new Review().setUserId(2), new User(), "comment"); - dispatcher.dispatch(notification, context); - verify(context).addUser(2); - } - -} diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/reviews/ReviewsNotificationManagerTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/reviews/ReviewsNotificationManagerTest.java index 3dd2e3e6f3b..8e0eaba6953 100644 --- a/sonar-server/src/test/java/org/sonar/server/notifications/reviews/ReviewsNotificationManagerTest.java +++ b/sonar-server/src/test/java/org/sonar/server/notifications/reviews/ReviewsNotificationManagerTest.java @@ -24,8 +24,8 @@ import static org.junit.Assert.assertThat; import org.junit.Before; import org.junit.Test; +import org.sonar.api.database.model.Review; import org.sonar.api.database.model.User; -import org.sonar.jpa.entity.Review; import org.sonar.jpa.test.AbstractDbUnitTestCase; public class ReviewsNotificationManagerTest extends AbstractDbUnitTestCase { |