* Persist notifications into DB for later delivery.
* Add sonar-email-plugin, which sends notifications by email.
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar</artifactId>
+ <version>2.10-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+
+ <groupId>org.codehaus.sonar.plugins</groupId>
+ <artifactId>sonar-email-plugin</artifactId>
+ <packaging>sonar-plugin</packaging>
+
+ <name>Sonar :: Plugins :: Email</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-email</artifactId>
+ <version>1.2</version>
+ </dependency>
+
+ <!-- unit tests -->
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <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>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginName>Email</pluginName>
+ <pluginClass>org.sonar.plugins.email.EmailPlugin</pluginClass>
+ <pluginDescription><![CDATA[TODO]]></pluginDescription>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /dev/null
+/*
+ * 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.plugins.email;
+
+import org.apache.commons.configuration.Configuration;
+import org.sonar.api.ServerExtension;
+
+/**
+ * Ruby uses constants from this class.
+ *
+ * @since 2.10
+ */
+public class EmailConfiguration implements ServerExtension {
+
+ 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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.plugins.email;
+
+import org.apache.commons.lang.StringUtils;
+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.DatabaseSession;
+import org.sonar.api.database.model.User;
+import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.api.notifications.Notification;
+import org.sonar.jpa.session.DatabaseSessionFactory;
+import org.sonar.plugins.email.api.EmailMessage;
+import org.sonar.plugins.email.api.EmailTemplate;
+
+/**
+ * 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 org.apache.commons.mail.Email#setSocketConnectionTimeout(int)
+ * @see org.apache.commons.mail.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 EmailTemplate[] templates;
+ private DatabaseSessionFactory sessionFactory;
+
+ public EmailNotificationChannel(EmailConfiguration configuration, EmailTemplate[] templates, DatabaseSessionFactory sessionFactory) {
+ this.configuration = configuration;
+ this.templates = templates;
+ this.sessionFactory = sessionFactory;
+ }
+
+ private User getUserByLogin(String login) {
+ DatabaseSession session = sessionFactory.getSession();
+ return session.getSingleResult(User.class, "login", login);
+ }
+
+ @Override
+ public void deliver(Notification notification, String username) {
+ EmailMessage emailMessage = format(notification, username);
+ if (emailMessage != null) {
+ deliver(emailMessage);
+ }
+ }
+
+ private EmailMessage format(Notification notification, String username) {
+ for (EmailTemplate template : templates) {
+ EmailMessage email = template.format(notification);
+ if (email != null) {
+ User user = getUserByLogin(username);
+ email.setTo(user.getEmail());
+ return email;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Visibility has been relaxed for tests.
+ */
+ void deliver(EmailMessage emailMessage) {
+ if (StringUtils.isBlank(configuration.getSmtpHost())) {
+ LOG.warn("SMTP host was not configured - email will not be sent");
+ return;
+ }
+ try {
+ send(emailMessage);
+ } 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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.plugins.email;
+
+import org.sonar.api.SonarPlugin;
+import org.sonar.plugins.email.reviews.CommentOnReviewAssignedToMe;
+import org.sonar.plugins.email.reviews.CommentOnReviewCreatedByMe;
+import org.sonar.plugins.email.reviews.CommentOnReviewEmailTemplate;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class EmailPlugin extends SonarPlugin {
+
+ public List getExtensions() {
+ return Arrays.asList(
+ EmailConfiguration.class,
+ EmailNotificationChannel.class,
+
+ CommentOnReviewEmailTemplate.class,
+ CommentOnReviewCreatedByMe.class,
+ CommentOnReviewAssignedToMe.class);
+ }
+
+}
--- /dev/null
+/*
+ * 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.plugins.email.api;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+/**
+ * @since 2.10
+ */
+public class EmailMessage {
+
+ 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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.plugins.email.api;
+
+import org.sonar.api.ServerExtension;
+import org.sonar.api.notifications.Notification;
+
+/**
+ * @since 2.10
+ */
+public abstract class EmailTemplate implements ServerExtension {
+
+ public abstract EmailMessage format(Notification notification);
+
+}
--- /dev/null
+/*
+ * 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.plugins.email.reviews;
+
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.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 ("review".equals(notification.getType())) {
+ context.addUser(notification.getFieldValue("assignee"));
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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.plugins.email.reviews;
+
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.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 ("review".equals(notification.getType())) {
+ context.addUser(notification.getFieldValue("creator"));
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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.plugins.email.reviews;
+
+import org.sonar.api.notifications.Notification;
+import org.sonar.plugins.email.api.EmailMessage;
+import org.sonar.plugins.email.api.EmailTemplate;
+
+public class CommentOnReviewEmailTemplate extends EmailTemplate {
+
+ @Override
+ public EmailMessage format(Notification notification) {
+ if ("review".equals(notification.getType())) {
+ String reviewId = notification.getFieldValue("reviewId");
+ String author = notification.getFieldValue("author");
+ String comment = notification.getFieldValue("comment");
+ EmailMessage email = new EmailMessage()
+ .setFrom(author)
+ .setMessageId("review/" + reviewId)
+ .setSubject("Review #" + reviewId)
+ .setMessage(comment);
+ return email;
+ }
+ return null;
+ }
+
+}
--- /dev/null
+/*
+ * 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.plugins.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 org.sonar.plugins.email.api.EmailMessage;
+
+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() throws Exception {
+ 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() throws Exception {
+ 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]");
+ }
+
+}
--- /dev/null
+/*
+ * 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.plugins.email.review;
+
+import static org.mockito.Mockito.mock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.notifications.NotificationDispatcher;
+import org.sonar.plugins.email.reviews.CommentOnReviewAssignedToMe;
+
+public class CommentOnReviewAssignedToMeTest { // FIXME implement me
+
+ 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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.plugins.email.review;
+
+import static org.mockito.Mockito.mock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.notifications.NotificationDispatcher;
+import org.sonar.plugins.email.reviews.CommentOnReviewCreatedByMe;
+
+public class CommentOnReviewCreatedByMeTest { // FIXME implement me
+
+ 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);
+ }
+
+}
<module>plugins/sonar-squid-java-plugin</module>
<module>plugins/sonar-design-plugin</module>
<module>plugins/sonar-i18n-en-plugin</module>
+ <module>plugins/sonar-email-plugin</module>
</modules>
<organization>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar.plugins</groupId>
+ <artifactId>sonar-email-plugin</artifactId>
+ <version>${project.version}</version>
+ <scope>runtime</scope>
+ </dependency>
<dependency>
<groupId>org.sonatype.jsw-binaries</groupId>
<artifactId>jsw-binaries</artifactId>
--- /dev/null
+/*
+ * 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.core.notifications;
+
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationManager;
+import org.sonar.jpa.entity.NotificationQueueElement;
+import org.sonar.jpa.session.DatabaseSessionFactory;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @since 2.10
+ */
+public class DefaultNotificationManager implements NotificationManager {
+
+ private DatabaseSessionFactory sessionFactory;
+
+ public DefaultNotificationManager(DatabaseSessionFactory sessionFactory) {
+ this.sessionFactory = sessionFactory;
+ }
+
+ public void scheduleForSending(Notification notification) {
+ NotificationQueueElement notificationQueueElement = new NotificationQueueElement();
+ notificationQueueElement.setCreatedAt(new Date());
+ notificationQueueElement.setNotification(notification);
+ DatabaseSession session = sessionFactory.getSession();
+ session.save(notificationQueueElement);
+ session.commit();
+ }
+
+ public NotificationQueueElement getFromQueue() {
+ DatabaseSession session = sessionFactory.getSession();
+ String hql = "FROM " + NotificationQueueElement.class.getSimpleName() + " ORDER BY createdAt ASC LIMIT 1";
+ List<NotificationQueueElement> notifications = session.createQuery(hql).getResultList();
+ if (notifications.isEmpty()) {
+ return null;
+ }
+ NotificationQueueElement notification = notifications.get(0);
+ session.removeWithoutFlush(notification);
+ session.commit();
+ return notification;
+ }
+
+}
--- /dev/null
+/*
+ * 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.jpa.entity;
+
+import org.sonar.api.notifications.Notification;
+
+import java.io.*;
+import java.util.Date;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "notifications")
+public class NotificationQueueElement {
+
+ @Id
+ @Column(name = "id")
+ @GeneratedValue
+ private Integer id;
+
+ @Column(name = "created_at")
+ private Date createdAt;
+
+ @Column(name = "data", updatable = true, nullable = true, length = 167772150)
+ private byte[] data;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public Date getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Date createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public void setNotification(Notification notification) {
+ try {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
+ objectOutputStream.writeObject(notification);
+ objectOutputStream.close();
+ this.data = byteArrayOutputStream.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Notification getNotification() {
+ if (this.data == null) {
+ return null;
+ }
+ try {
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.data);
+ ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
+ Object result = objectInputStream.readObject();
+ objectInputStream.close();
+ return (Notification) result;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
+++ /dev/null
-/*
- * 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.jpa.entity;
-
-import javax.persistence.*;
-
-@Entity
-@Table(name = "reviews")
-public final class Review {
-
- @Id
- @Column(name = "id")
- @GeneratedValue
- private Long id;
-
- @Column(name = "user_id")
- private Integer userId;
-
- @Column(name = "assignee_id")
- private Integer assigneeId;
-
- @Column(name = "title")
- private String title;
-
- /**
- * @return id of review
- */
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- /**
- * @return id of user, who created this review
- */
- public Integer getUserId() {
- return userId;
- }
-
- public Review setUserId(Integer userId) {
- this.userId = userId;
- return this;
- }
-
- /**
- * @return id of assigned user or null, if not assigned
- */
- public Integer getAssigneeId() {
- return assigneeId;
- }
-
- public Review setAssigneeId(Integer assigneeId) {
- this.assigneeId = assigneeId;
- return this;
- }
-
- public String getTitle() {
- return title;
- }
-
- public Review setTitle(String title) {
- this.title = title;
- return this;
- }
-
-}
- complete the Derby DDL file used for unit tests : sonar-testing-harness/src/main/resources/org/sonar/test/persistence/sonar-test.ddl
*/
- public static final int LAST_VERSION = 214;
+ public static final int LAST_VERSION = 215;
public final static String TABLE_NAME = "schema_migrations";
<class>org.sonar.api.profiles.Alert</class>
<class>org.sonar.api.rules.ActiveRuleChange</class>
<class>org.sonar.api.rules.ActiveRuleParamChange</class>
- <class>org.sonar.jpa.entity.Review</class>
+ <class>org.sonar.api.database.model.Review</class>
+ <class>org.sonar.jpa.entity.NotificationQueueElement</class>
<properties>
<property name="hibernate.current_session_context_class" value="thread"/>
--- /dev/null
+/*
+ * 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.core.notifications;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.notifications.Notification;
+import org.sonar.jpa.entity.NotificationQueueElement;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+public class DefaultNotificationManagerTest extends AbstractDbUnitTestCase {
+
+ private DefaultNotificationManager manager;
+
+ @Before
+ public void setUp() {
+ manager = new DefaultNotificationManager(getSessionFactory());
+ }
+
+ @Test
+ public void shouldPersist() throws Exception {
+ Notification notification = new Notification("test");
+ manager.scheduleForSending(notification);
+
+ NotificationQueueElement queueElement = manager.getFromQueue();
+ assertThat(queueElement.getNotification(), is(notification));
+
+ assertThat(manager.getFromQueue(), nullValue());
+ }
+
+}
--- /dev/null
+/*
+ * 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.api.database.model;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "reviews")
+public final class Review {
+
+ @Id
+ @Column(name = "id")
+ @GeneratedValue
+ private Long id;
+
+ @Column(name = "user_id")
+ private Integer userId;
+
+ @Column(name = "assignee_id")
+ private Integer assigneeId;
+
+ @Column(name = "title")
+ private String title;
+
+ /**
+ * @return id of review
+ */
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ /**
+ * @return id of user, who created this review
+ */
+ public Integer getUserId() {
+ return userId;
+ }
+
+ public Review setUserId(Integer userId) {
+ this.userId = userId;
+ return this;
+ }
+
+ /**
+ * @return id of assigned user or null, if not assigned
+ */
+ public Integer getAssigneeId() {
+ return assigneeId;
+ }
+
+ public Review setAssigneeId(Integer assigneeId) {
+ this.assigneeId = assigneeId;
+ return this;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public Review setTitle(String title) {
+ this.title = title;
+ return this;
+ }
+
+}
--- /dev/null
+/*
+ * 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.api.notifications;
+
+import com.google.common.collect.Maps;
+import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * @since 2.10
+ */
+public class Notification implements Serializable {
+
+ private String type;
+
+ private Map<String, String> fields = Maps.newHashMap();
+
+ public Notification(String type) {
+ this.type = type;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public Notification setFieldValue(String field, String value) {
+ fields.put(field, value);
+ return this;
+ }
+
+ public String getFieldValue(String field) {
+ return fields.get(field);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Notification)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ Notification other = (Notification) obj;
+ return this.type.equals(other.type) && this.fields.equals(other.fields);
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode() * 31 + fields.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
+ }
+
+}
--- /dev/null
+/*
+ * 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.api.notifications;
+
+import org.sonar.api.ServerExtension;
+
+/**
+ * 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 void deliver(Notification notification, String username);
+
+}
--- /dev/null
+/*
+ * 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.api.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(String username);
+ }
+
+ /**
+ * @return unique key of this dispatcher
+ */
+ public String getKey() {
+ return getClass().getSimpleName();
+ }
+
+ /**
+ * @return recipients
+ */
+ public abstract void dispatch(Notification notification, Context context);
+
+}
--- /dev/null
+/*
+ * 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.api.notifications;
+
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+
+/**
+ * @since 2.10
+ */
+public interface NotificationManager extends ServerComponent, BatchComponent {
+
+ void scheduleForSending(Notification notification);
+
+}
<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>
<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>
<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>
+++ /dev/null
-/*
- * 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 {
-}
+++ /dev/null
-/*
- * 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);
-
-}
+++ /dev/null
-/*
- * 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);
-
-}
+++ /dev/null
-/*
- * 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
- }
-
-}
+++ /dev/null
-/*
- * 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));
- }
-
-}
*/
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;
*/
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;
}
/**
* 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
}
}
+++ /dev/null
-/*
- * 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);
- }
-
-}
+++ /dev/null
-/*
- * 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();
- }
-
-}
+++ /dev/null
-/*
- * 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);
-
-}
+++ /dev/null
-/*
- * 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);
- }
-
-}
+++ /dev/null
-/*
- * 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());
- }
- }
- }
-
-}
+++ /dev/null
-/*
- * 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());
- }
- }
-
-}
+++ /dev/null
-/*
- * 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;
- }
-
-}
+++ /dev/null
-/*
- * 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;
- }
-
-}
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
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);
}
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;
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;
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();
}
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
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
redirect_to :action => 'index'
end
+ private
+
+ def configuration
+ java_facade.getComponentByClassname('email', 'org.sonar.plugins.email.EmailConfiguration').class
+ end
+
end
--- /dev/null
+#
+# 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
+++ /dev/null
-/*
- * 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);
- }
-
-}
*/
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);
+ }
+
}
+++ /dev/null
-/*
- * 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]");
- }
-
-}
+++ /dev/null
-/*
- * 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);
- }
-
-}
+++ /dev/null
-/*
- * 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);
- }
-
-}
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 {