]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-2596,SONAR-2600 Improve notification mechanism
authorEvgeny Mandrikov <mandrikov@gmail.com>
Tue, 19 Jul 2011 09:09:51 +0000 (13:09 +0400)
committerEvgeny Mandrikov <mandrikov@gmail.com>
Wed, 20 Jul 2011 15:02:49 +0000 (19:02 +0400)
* Persist notifications into DB for later delivery.
* Add sonar-email-plugin, which sends notifications by email.

50 files changed:
plugins/sonar-email-plugin/pom.xml [new file with mode: 0644]
plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/EmailConfiguration.java [new file with mode: 0644]
plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/EmailNotificationChannel.java [new file with mode: 0644]
plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/EmailPlugin.java [new file with mode: 0644]
plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/api/EmailMessage.java [new file with mode: 0644]
plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/api/EmailTemplate.java [new file with mode: 0644]
plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/reviews/CommentOnReviewAssignedToMe.java [new file with mode: 0644]
plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/reviews/CommentOnReviewCreatedByMe.java [new file with mode: 0644]
plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/reviews/CommentOnReviewEmailTemplate.java [new file with mode: 0644]
plugins/sonar-email-plugin/src/test/java/org/sonar/plugins/email/EmailNotificationChannelTest.java [new file with mode: 0644]
plugins/sonar-email-plugin/src/test/java/org/sonar/plugins/email/review/CommentOnReviewAssignedToMeTest.java [new file with mode: 0644]
plugins/sonar-email-plugin/src/test/java/org/sonar/plugins/email/review/CommentOnReviewCreatedByMeTest.java [new file with mode: 0644]
pom.xml
sonar-application/pom.xml
sonar-core/src/main/java/org/sonar/core/notifications/DefaultNotificationManager.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/jpa/entity/NotificationQueueElement.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/jpa/entity/Review.java [deleted file]
sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java
sonar-core/src/main/resources/META-INF/persistence.xml
sonar-core/src/test/java/org/sonar/core/notifications/DefaultNotificationManagerTest.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/database/model/Review.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/notifications/Notification.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/notifications/NotificationChannel.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/notifications/NotificationDispatcher.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/notifications/NotificationManager.java [new file with mode: 0644]
sonar-server/pom.xml
sonar-server/src/main/java/org/sonar/server/notifications/Notification.java [deleted file]
sonar-server/src/main/java/org/sonar/server/notifications/NotificationChannel.java [deleted file]
sonar-server/src/main/java/org/sonar/server/notifications/NotificationDispatcher.java [deleted file]
sonar-server/src/main/java/org/sonar/server/notifications/NotificationManager.java [deleted file]
sonar-server/src/main/java/org/sonar/server/notifications/NotificationQueue.java [deleted file]
sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java
sonar-server/src/main/java/org/sonar/server/notifications/email/EmailConfiguration.java [deleted file]
sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java [deleted file]
sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessageTemplate.java [deleted file]
sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java [deleted file]
sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMe.java [deleted file]
sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMe.java [deleted file]
sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java [deleted file]
sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewNotification.java [deleted file]
sonar-server/src/main/java/org/sonar/server/notifications/reviews/ReviewsNotificationManager.java
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/email_configuration_controller.rb
sonar-server/src/main/webapp/WEB-INF/db/migrate/215_create_notifications.rb [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/notifications/NotificationManagerTest.java [deleted file]
sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java
sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java [deleted file]
sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMeTest.java [deleted file]
sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMeTest.java [deleted file]
sonar-server/src/test/java/org/sonar/server/notifications/reviews/ReviewsNotificationManagerTest.java

diff --git a/plugins/sonar-email-plugin/pom.xml b/plugins/sonar-email-plugin/pom.xml
new file mode 100644 (file)
index 0000000..7a6fc71
--- /dev/null
@@ -0,0 +1,63 @@
+<?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>
diff --git a/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/EmailConfiguration.java b/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/EmailConfiguration.java
new file mode 100644 (file)
index 0000000..a2c5a45
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/EmailNotificationChannel.java b/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/EmailNotificationChannel.java
new file mode 100644 (file)
index 0000000..cb31280
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/EmailPlugin.java b/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/EmailPlugin.java
new file mode 100644 (file)
index 0000000..4da910b
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/api/EmailMessage.java b/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/api/EmailMessage.java
new file mode 100644 (file)
index 0000000..556c72b
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/api/EmailTemplate.java b/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/api/EmailTemplate.java
new file mode 100644 (file)
index 0000000..283eaf9
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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);
+
+}
diff --git a/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/reviews/CommentOnReviewAssignedToMe.java b/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/reviews/CommentOnReviewAssignedToMe.java
new file mode 100644 (file)
index 0000000..9505153
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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"));
+    }
+  }
+
+}
diff --git a/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/reviews/CommentOnReviewCreatedByMe.java b/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/reviews/CommentOnReviewCreatedByMe.java
new file mode 100644 (file)
index 0000000..c91ea5f
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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"));
+    }
+  }
+
+}
diff --git a/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/reviews/CommentOnReviewEmailTemplate.java b/plugins/sonar-email-plugin/src/main/java/org/sonar/plugins/email/reviews/CommentOnReviewEmailTemplate.java
new file mode 100644 (file)
index 0000000..4f2d4c8
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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;
+  }
+
+}
diff --git a/plugins/sonar-email-plugin/src/test/java/org/sonar/plugins/email/EmailNotificationChannelTest.java b/plugins/sonar-email-plugin/src/test/java/org/sonar/plugins/email/EmailNotificationChannelTest.java
new file mode 100644 (file)
index 0000000..a33b50e
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * 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]");
+  }
+
+}
diff --git a/plugins/sonar-email-plugin/src/test/java/org/sonar/plugins/email/review/CommentOnReviewAssignedToMeTest.java b/plugins/sonar-email-plugin/src/test/java/org/sonar/plugins/email/review/CommentOnReviewAssignedToMeTest.java
new file mode 100644 (file)
index 0000000..2c31c1f
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/plugins/sonar-email-plugin/src/test/java/org/sonar/plugins/email/review/CommentOnReviewCreatedByMeTest.java b/plugins/sonar-email-plugin/src/test/java/org/sonar/plugins/email/review/CommentOnReviewCreatedByMeTest.java
new file mode 100644 (file)
index 0000000..2d34512
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/pom.xml b/pom.xml
index cb9c6431d9ec57b606ac0a2ab8398f3bbef347c1..857584c86a2504cc8206d90c7e1c36d2df0ae5d5 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,7 @@
     <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>
index 32072863f928642157bd8cf83996237ee17bfb6e..407cb8aeefb4096012473ca97d4c8486593da2bc 100644 (file)
       <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>
diff --git a/sonar-core/src/main/java/org/sonar/core/notifications/DefaultNotificationManager.java b/sonar-core/src/main/java/org/sonar/core/notifications/DefaultNotificationManager.java
new file mode 100644 (file)
index 0000000..4f8b604
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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;
+  }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/entity/NotificationQueueElement.java b/sonar-core/src/main/java/org/sonar/jpa/entity/NotificationQueueElement.java
new file mode 100644 (file)
index 0000000..198cd08
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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);
+    }
+  }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/entity/Review.java b/sonar-core/src/main/java/org/sonar/jpa/entity/Review.java
deleted file mode 100644 (file)
index 77dce34..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.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;
-  }
-
-}
index a77283fd703223e3bd926c90075d0b633f947a10..592534f56d7241484fdcb1740614dcfc8d2f30fa 100644 (file)
@@ -40,7 +40,7 @@ public class SchemaMigration {
       - 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";
 
index bd7a593133d4209b8bffa111c4ffb02ca478b6d3..f3b022a96a329451f8ece7ed1238df7a30db13a8 100644 (file)
@@ -34,7 +34,8 @@
     <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"/>
diff --git a/sonar-core/src/test/java/org/sonar/core/notifications/DefaultNotificationManagerTest.java b/sonar-core/src/test/java/org/sonar/core/notifications/DefaultNotificationManagerTest.java
new file mode 100644 (file)
index 0000000..70587fa
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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());
+  }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/Review.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/Review.java
new file mode 100644 (file)
index 0000000..dc92a98
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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;
+  }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/notifications/Notification.java b/sonar-plugin-api/src/main/java/org/sonar/api/notifications/Notification.java
new file mode 100644 (file)
index 0000000..8ec915c
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/notifications/NotificationChannel.java b/sonar-plugin-api/src/main/java/org/sonar/api/notifications/NotificationChannel.java
new file mode 100644 (file)
index 0000000..5be1149
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/notifications/NotificationDispatcher.java b/sonar-plugin-api/src/main/java/org/sonar/api/notifications/NotificationDispatcher.java
new file mode 100644 (file)
index 0000000..6886725
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/notifications/NotificationManager.java b/sonar-plugin-api/src/main/java/org/sonar/api/notifications/NotificationManager.java
new file mode 100644 (file)
index 0000000..bc4e365
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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);
+
+}
index df611dabce4248a24a0e257d7f6bbb06c0558114..6f6f74371af6e9d5d223be9f1dac3c013fd7e21e 100644 (file)
       <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>
 
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/Notification.java b/sonar-server/src/main/java/org/sonar/server/notifications/Notification.java
deleted file mode 100644 (file)
index 395f90a..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications;
-
-/**
- * Marker interface for data model objects that represents notifications.
- * 
- * @since 2.10
- */
-public interface Notification {
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationChannel.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationChannel.java
deleted file mode 100644 (file)
index 1b27267..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications;
-
-import org.sonar.api.ServerExtension;
-
-import java.io.Serializable;
-
-/**
- * Provides logic to deliver notification.
- * For example:
- * <ul>
- * <li>email - sends email as soon as possible</li>
- * <li>email (digest) - collects notifications and sends them together once a day</li>
- * <li>gtalk - sends a chat message as soon as possible</li>
- * </ul>
- * 
- * @since 2.10
- */
-public abstract class NotificationChannel implements ServerExtension {
-
-  /**
-   * @return unique key of this channel
-   */
-  public String getKey() {
-    return getClass().getSimpleName();
-  }
-
-  public abstract Serializable createDataForPersistance(Notification notification, Integer userId);
-
-  public abstract void deliver(Serializable notificationData);
-
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationDispatcher.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationDispatcher.java
deleted file mode 100644 (file)
index a82ee00..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications;
-
-import org.sonar.api.ServerExtension;
-
-/**
- * Provides logic to determine which users are interested in receiving notification.
- * Has no knowledge about the way of delivery.
- * For example:
- * <ul>
- * <li>notify me when someone comments on review created by me</li>
- * <li>notify me when someone comments on review assigned to me</li>
- * <li>notify me when someone mentions me in comment for review</li>
- * <li>send me system notifications (like password reset, account creation, ...)</li>
- * </ul> 
- * 
- * @since 2.10
- */
-public abstract class NotificationDispatcher implements ServerExtension {
-
-  public interface Context {
-    void addUser(Integer userId);
-  }
-
-  /**
-   * @return unique key of this dispatcher
-   */
-  public String getKey() {
-    return getClass().getSimpleName();
-  }
-
-  /**
-   * @return recipients
-   */
-  public abstract void dispatch(Notification notification, Context context);
-
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationManager.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationManager.java
deleted file mode 100644 (file)
index 304f3c6..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Sets;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.ServerComponent;
-import org.sonar.api.utils.TimeProfiler;
-
-import java.io.Serializable;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @since 2.10
- */
-public class NotificationManager implements ServerComponent { // TODO should be available on batch side too
-
-  private static final Logger LOG = LoggerFactory.getLogger(NotificationManager.class);
-  private static final TimeProfiler TIME_PROFILER = new TimeProfiler().setLogger(LOG);
-
-  private NotificationQueue queue;
-  private NotificationDispatcher[] dispatchers;
-  private NotificationChannel[] channels;
-
-  public NotificationManager(NotificationQueue queue, NotificationDispatcher[] dispatchers, NotificationChannel[] channels) {
-    this.queue = queue;
-    this.dispatchers = dispatchers;
-    this.channels = channels;
-  }
-
-  public void scheduleForSending(Notification notification) {
-    TIME_PROFILER.start("Scheduling " + notification);
-    SetMultimap<Integer, NotificationChannel> recipients = HashMultimap.create();
-    for (NotificationChannel channel : channels) {
-      for (NotificationDispatcher dispatcher : dispatchers) {
-        final Set<Integer> possibleRecipients = Sets.newHashSet();
-        NotificationDispatcher.Context context = new NotificationDispatcher.Context() {
-          public void addUser(Integer userId) {
-            possibleRecipients.add(userId);
-          }
-        };
-        dispatcher.dispatch(notification, context);
-        for (Integer userId : possibleRecipients) {
-          if (isEnabled(userId, channel, dispatcher)) {
-            recipients.put(userId, channel);
-          }
-        }
-      }
-    }
-    for (Map.Entry<Integer, NotificationChannel> entry : recipients.entries()) {
-      Integer userId = entry.getKey();
-      NotificationChannel channel = entry.getValue();
-      LOG.info("For user {} via {}", userId, channel.getKey());
-      Serializable notificationData = channel.createDataForPersistance(notification, userId);
-      queue.add(notificationData, channel);
-    }
-    TIME_PROFILER.stop();
-  }
-
-  boolean isEnabled(Integer userId, NotificationChannel channel, NotificationDispatcher dispatcher) {
-    return true; // FIXME for the moment we will accept everything
-  }
-
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationQueue.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationQueue.java
deleted file mode 100644 (file)
index bf02333..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications;
-
-import org.sonar.api.ServerComponent;
-
-import java.io.Serializable;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-/**
- * @since 2.10
- */
-public class NotificationQueue implements ServerComponent {
-
-  private ConcurrentLinkedQueue<Element> queue = new ConcurrentLinkedQueue<Element>();
-
-  public static class Element {
-    String channelKey;
-    Serializable notificationData;
-
-    public Element(String channelKey, Serializable notificationData) {
-      this.channelKey = channelKey;
-      this.notificationData = notificationData;
-    }
-  }
-
-  public Element get() {
-    return queue.poll();
-  }
-
-  public void add(Serializable notificationData, NotificationChannel channel) {
-    queue.add(new Element(channel.getKey(), notificationData));
-  }
-
-}
index 6faab4f133f9a345ffdb8da77ca508c47c4fce59..b77fe0e629648e20b8bd3afa3751659e728d5d61 100644 (file)
  */
 package org.sonar.server.notifications;
 
-import com.google.common.collect.Maps;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.ServerComponent;
-import org.sonar.server.notifications.NotificationQueue.Element;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.api.notifications.NotificationDispatcher;
+import org.sonar.api.utils.TimeProfiler;
+import org.sonar.core.notifications.DefaultNotificationManager;
+import org.sonar.jpa.entity.NotificationQueueElement;
 
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
@@ -35,27 +43,28 @@ import java.util.concurrent.TimeUnit;
  */
 public class NotificationService implements ServerComponent {
 
-  private static Logger LOG = LoggerFactory.getLogger(NotificationService.class);
+  private static final Logger LOG = LoggerFactory.getLogger(NotificationService.class);
+  private static final TimeProfiler TIME_PROFILER = new TimeProfiler(LOG);
 
   private ScheduledExecutorService executorService;
   private long period = 10; // FIXME small value just for tests
-  private NotificationQueue queue;
 
-  private Map<String, NotificationChannel> channels = Maps.newHashMap();
+  private DefaultNotificationManager manager;
+  private NotificationChannel[] channels;
+  private NotificationDispatcher[] dispatchers;
 
   /**
    * Default constructor when no channels.
    */
-  public NotificationService(NotificationQueue queue) {
-    this(queue, new NotificationChannel[0]);
+  public NotificationService(DefaultNotificationManager manager, NotificationDispatcher[] dispatchers) {
+    this(manager, dispatchers, new NotificationChannel[0]);
     LOG.warn("There is no channels - all notifications would be ignored!");
   }
 
-  public NotificationService(NotificationQueue queue, NotificationChannel[] channels) {
-    this.queue = queue;
-    for (NotificationChannel channel : channels) {
-      this.channels.put(channel.getKey(), channel);
-    }
+  public NotificationService(DefaultNotificationManager manager, NotificationDispatcher[] dispatchers, NotificationChannel[] channels) {
+    this.manager = manager;
+    this.channels = channels;
+    this.dispatchers = dispatchers;
   }
 
   /**
@@ -89,21 +98,48 @@ public class NotificationService implements ServerComponent {
    * Visibility has been relaxed for tests.
    */
   void processQueue() {
-    NotificationQueue.Element element = queue.get();
-    while (element != null) {
-      deliver(element);
-      element = queue.get();
+    NotificationQueueElement queueElement = manager.getFromQueue();
+    while (queueElement != null) {
+      deliver(queueElement.getNotification());
+      queueElement = manager.getFromQueue();
     }
   }
 
   /**
    * Visibility has been relaxed for tests.
    */
-  void deliver(Element element) {
-    NotificationChannel channel = channels.get(element.channelKey);
-    if (channel != null) {
-      channel.deliver(element.notificationData);
+  void deliver(Notification notification) {
+    TIME_PROFILER.start("Delivering notification " + notification);
+    SetMultimap<String, NotificationChannel> recipients = HashMultimap.create();
+    for (NotificationChannel channel : channels) {
+      for (NotificationDispatcher dispatcher : dispatchers) {
+        final Set<String> possibleRecipients = Sets.newHashSet();
+        NotificationDispatcher.Context context = new NotificationDispatcher.Context() {
+          public void addUser(String username) {
+            if (username != null) {
+              possibleRecipients.add(username);
+            }
+          }
+        };
+        dispatcher.dispatch(notification, context);
+        for (String username : possibleRecipients) {
+          if (isEnabled(username, channel, dispatcher)) {
+            recipients.put(username, channel);
+          }
+        }
+      }
     }
+    for (Map.Entry<String, NotificationChannel> entry : recipients.entries()) {
+      String username = entry.getKey();
+      NotificationChannel channel = entry.getValue();
+      LOG.info("For user {} via {}", username, channel);
+      channel.deliver(notification, username);
+    }
+    TIME_PROFILER.stop();
+  }
+
+  boolean isEnabled(String username, NotificationChannel channel, NotificationDispatcher dispatcher) {
+    return true; // FIXME for the moment we will accept everything
   }
 
 }
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailConfiguration.java b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailConfiguration.java
deleted file mode 100644 (file)
index e16e536..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications.email;
-
-import org.apache.commons.configuration.Configuration;
-import org.sonar.api.ServerComponent;
-
-/**
- * Ruby uses constants from this class.
- * 
- * @since 2.10
- */
-public class EmailConfiguration implements ServerComponent {
-
-  public static final String SMTP_HOST = "email.smtp_host";
-  public static final String SMTP_HOST_DEFAULT = "";
-  public static final String SMTP_PORT = "email.smtp_port";
-  public static final String SMTP_PORT_DEFAULT = "25";
-  public static final String SMTP_USE_TLS = "email.smtp_use_tls";
-  public static final boolean SMTP_USE_TLS_DEFAULT = false;
-  public static final String SMTP_USERNAME = "email.smtp_username";
-  public static final String SMTP_USERNAME_DEFAULT = "";
-  public static final String SMTP_PASSWORD = "email.smtp_password";
-  public static final String SMTP_PASSWORD_DEFAULT = "";
-  public static final String FROM = "email.from";
-  public static final String FROM_DEFAULT = "noreply@nowhere";
-  public static final String PREFIX = "email.prefix";
-  public static final String PREFIX_DEFAULT = "[SONAR]";
-
-  private Configuration configuration;
-
-  public EmailConfiguration(Configuration configuration) {
-    this.configuration = configuration;
-  }
-
-  public String getSmtpHost() {
-    return configuration.getString(SMTP_HOST, SMTP_HOST_DEFAULT);
-  }
-
-  public String getSmtpPort() {
-    return configuration.getString(SMTP_PORT, SMTP_PORT_DEFAULT);
-  }
-
-  public boolean isUseTLS() {
-    return configuration.getBoolean(SMTP_USE_TLS, SMTP_USE_TLS_DEFAULT);
-  }
-
-  public String getSmtpUsername() {
-    return configuration.getString(SMTP_USERNAME, SMTP_USERNAME_DEFAULT);
-  }
-
-  public String getSmtpPassword() {
-    return configuration.getString(SMTP_PASSWORD, SMTP_PASSWORD_DEFAULT);
-  }
-
-  public String getFrom() {
-    return configuration.getString(FROM, FROM_DEFAULT);
-  }
-
-  public String getPrefix() {
-    return configuration.getString(PREFIX, PREFIX_DEFAULT);
-  }
-
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java
deleted file mode 100644 (file)
index c2227e6..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications.email;
-
-import org.apache.commons.lang.builder.ToStringBuilder;
-
-import java.io.Serializable;
-
-/**
- * @since 2.10
- */
-public class EmailMessage implements Serializable {
-
-  private String from;
-  private String to;
-  private String subject;
-  private String message;
-  private String messageId;
-
-  /**
-   * @param from full name of user, who initiated this message or null, if message was initiated by Sonar
-   */
-  public EmailMessage setFrom(String from) {
-    this.from = from;
-    return this;
-  }
-
-  /**
-   * @see #setFrom(String)
-   */
-  public String getFrom() {
-    return from;
-  }
-
-  /**
-   * @param to email address where to send this message
-   */
-  public EmailMessage setTo(String to) {
-    this.to = to;
-    return this;
-  }
-
-  /**
-   * @see #setTo(String)
-   */
-  public String getTo() {
-    return to;
-  }
-
-  /**
-   * @param subject message subject
-   */
-  public EmailMessage setSubject(String subject) {
-    this.subject = subject;
-    return this;
-  }
-
-  /**
-   * @see #setSubject(String)
-   */
-  public String getSubject() {
-    return subject;
-  }
-
-  /**
-   * @param message message body
-   */
-  public EmailMessage setMessage(String message) {
-    this.message = message;
-    return this;
-  }
-
-  /**
-   * @see #setMessage(String)
-   */
-  public String getMessage() {
-    return message;
-  }
-
-  /**
-   * @param messageId id of message for threading
-   */
-  public EmailMessage setMessageId(String messageId) {
-    this.messageId = messageId;
-    return this;
-  }
-
-  /**
-   * @see #setMessageId(String)
-   */
-  public String getMessageId() {
-    return messageId;
-  }
-
-  @Override
-  public String toString() {
-    return ToStringBuilder.reflectionToString(this).toString();
-  }
-
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessageTemplate.java b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessageTemplate.java
deleted file mode 100644 (file)
index ed465c7..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications.email;
-
-import org.sonar.api.ServerExtension;
-import org.sonar.server.notifications.Notification;
-
-/**
- * @since 2.10
- */
-public abstract class EmailMessageTemplate implements ServerExtension {
-
-  public abstract EmailMessage format(Notification notification);
-
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java
deleted file mode 100644 (file)
index 7c07f56..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications.email;
-
-import org.apache.commons.lang.StringUtils;
-import org.apache.commons.mail.Email;
-import org.apache.commons.mail.EmailException;
-import org.apache.commons.mail.SimpleEmail;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.database.model.User;
-import org.sonar.jpa.session.DatabaseSessionFactory;
-import org.sonar.server.notifications.Notification;
-import org.sonar.server.notifications.NotificationChannel;
-
-import java.io.Serializable;
-
-/**
- * References:
- * <ul>
- * <li><a href="http://tools.ietf.org/html/rfc4021">Registration of Mail and MIME Header Fields</a></li>
- * <li><a href="http://tools.ietf.org/html/rfc2919">List-Id: A Structured Field and Namespace for the Identification of Mailing Lists</a></li>
- * <li><a href="https://github.com/blog/798-threaded-email-notifications">GitHub: Threaded Email Notifications</a></li>
- * </ul>
- * 
- * @since 2.10
- */
-public class EmailNotificationChannel extends NotificationChannel {
-
-  private static final Logger LOG = LoggerFactory.getLogger(EmailNotificationChannel.class);
-
-  /**
-   * @see Email#setSocketConnectionTimeout(int)
-   * @see Email#setSocketTimeout(int)
-   */
-  private static final int SOCKET_TIMEOUT = 30000;
-
-  private static final String FROM_NAME_DEFAULT = "Sonar";
-  private static final String SUBJECT_DEFAULT = "Notification";
-
-  private EmailConfiguration configuration;
-  private EmailMessageTemplate[] templates;
-  private DatabaseSessionFactory sessionFactory;
-
-  public EmailNotificationChannel(EmailConfiguration configuration, EmailMessageTemplate[] templates, DatabaseSessionFactory sessionFactory) {
-    this.configuration = configuration;
-    this.templates = templates;
-    this.sessionFactory = sessionFactory;
-  }
-
-  private User getUserById(Integer id) {
-    return sessionFactory.getSession().getEntity(User.class, id);
-  }
-
-  @Override
-  public Serializable createDataForPersistance(Notification notification, Integer userId) {
-    for (EmailMessageTemplate template : templates) {
-      EmailMessage email = template.format(notification);
-      if (email != null) {
-        User user = getUserById(userId);
-        email.setTo(user.getEmail());
-        return email;
-      }
-    }
-    return null;
-  }
-
-  @Override
-  public void deliver(Serializable notificationData) {
-    if (StringUtils.isBlank(configuration.getSmtpHost())) {
-      LOG.warn("SMTP host was not configured - email will not be sent");
-      return;
-    }
-    try {
-      send((EmailMessage) notificationData);
-    } catch (EmailException e) {
-      LOG.error("Unable to send email", e);
-    }
-  }
-
-  private void send(EmailMessage emailMessage) throws EmailException {
-    LOG.info("Sending email: {}", emailMessage);
-    // TODO
-    String domain = "nemo.sonarsource.org";
-    String listId = "<sonar." + domain + ">";
-    String serverUrl = "http://nemo.sonarsource.org";
-
-    SimpleEmail email = new SimpleEmail();
-    /*
-     * Set headers for proper threading:
-     * GMail will not group messages, even if they have same subject, but don't have "In-Reply-To" and "References" headers.
-     * TODO investigate threading in other clients like KMail, Thunderbird, Outlook
-     */
-    if (StringUtils.isNotEmpty(emailMessage.getMessageId())) {
-      String messageId = "<" + emailMessage.getMessageId() + "@" + domain + ">";
-      email.addHeader("In-Reply-To", messageId);
-      email.addHeader("References", messageId);
-    }
-    // Set headers for proper filtering
-    email.addHeader("List-Id", listId);
-    email.addHeader("List-Archive", serverUrl);
-    // Set general information
-    email.setFrom(configuration.getFrom(), StringUtils.defaultIfBlank(emailMessage.getFrom(), FROM_NAME_DEFAULT));
-    email.addTo(emailMessage.getTo(), " ");
-    String subject = StringUtils.defaultIfBlank(StringUtils.trimToEmpty(configuration.getPrefix()) + " ", "")
-          + StringUtils.defaultString(emailMessage.getSubject(), SUBJECT_DEFAULT);
-    email.setSubject(subject);
-    email.setMsg(emailMessage.getMessage());
-    // Send
-    email.setHostName(configuration.getSmtpHost());
-    email.setSmtpPort(Integer.parseInt(configuration.getSmtpPort()));
-    email.setTLS(configuration.isUseTLS());
-    if (StringUtils.isNotBlank(configuration.getSmtpUsername()) || StringUtils.isNotBlank(configuration.getSmtpPassword())) {
-      email.setAuthentication(configuration.getSmtpUsername(), configuration.getSmtpPassword());
-    }
-    email.setSocketConnectionTimeout(SOCKET_TIMEOUT);
-    email.setSocketTimeout(SOCKET_TIMEOUT);
-    email.send();
-  }
-
-  /**
-   * Send test email. This method called from Ruby.
-   * 
-   * @throws EmailException when unable to send
-   */
-  public void sendTestEmail(String toAddress, String subject, String message) throws EmailException {
-    EmailMessage emailMessage = new EmailMessage();
-    emailMessage.setTo(toAddress);
-    emailMessage.setSubject(subject);
-    emailMessage.setMessage(message);
-    send(emailMessage);
-  }
-
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMe.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMe.java
deleted file mode 100644 (file)
index 51af872..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications.reviews;
-
-import org.sonar.jpa.entity.Review;
-import org.sonar.server.notifications.Notification;
-import org.sonar.server.notifications.NotificationDispatcher;
-
-/**
- * This dispatcher means: "notify me when when someone comments on review assigned to me". 
- * 
- * @since 2.10
- */
-public class CommentOnReviewAssignedToMe extends NotificationDispatcher {
-
-  @Override
-  public void dispatch(Notification notification, Context context) {
-    if (notification instanceof CommentOnReviewNotification) {
-      Review review = ((CommentOnReviewNotification) notification).getReview();
-      if (review.getAssigneeId() != null) {
-        context.addUser(review.getAssigneeId());
-      }
-    }
-  }
-
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMe.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMe.java
deleted file mode 100644 (file)
index 868e628..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications.reviews;
-
-import org.sonar.jpa.entity.Review;
-import org.sonar.server.notifications.Notification;
-import org.sonar.server.notifications.NotificationDispatcher;
-
-/**
- * This dispatcher means: "notify me when when someone comments on review created by me".
- * 
- * @since 2.10
- */
-public class CommentOnReviewCreatedByMe extends NotificationDispatcher {
-
-  @Override
-  public void dispatch(Notification notification, Context context) {
-    if (notification instanceof CommentOnReviewNotification) {
-      Review review = ((CommentOnReviewNotification) notification).getReview();
-      context.addUser(review.getUserId());
-    }
-  }
-
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java
deleted file mode 100644 (file)
index 460db32..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications.reviews;
-
-import org.sonar.server.notifications.Notification;
-import org.sonar.server.notifications.email.EmailMessage;
-import org.sonar.server.notifications.email.EmailMessageTemplate;
-
-/**
- * Email template for {@link CommentOnReviewNotification}.
- */
-public class CommentOnReviewEmailTemplate extends EmailMessageTemplate {
-
-  @Override
-  public EmailMessage format(Notification notification) {
-    if (notification instanceof CommentOnReviewNotification) {
-      CommentOnReviewNotification event = (CommentOnReviewNotification) notification;
-      EmailMessage email = new EmailMessage()
-          .setFrom(event.getAuthor().getName())
-          .setMessageId("review/" + event.getReview().getId())
-          .setSubject("Review #" + event.getReview().getId())
-          .setMessage(event.getComment());
-      return email;
-    }
-    return null;
-  }
-
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewNotification.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewNotification.java
deleted file mode 100644 (file)
index 48a89b3..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications.reviews;
-
-import org.sonar.api.database.model.User;
-import org.sonar.jpa.entity.Review;
-import org.sonar.server.notifications.Notification;
-
-/**
- * @since 2.10
- */
-public class CommentOnReviewNotification implements Notification {
-
-  private Review review;
-  private User author;
-  private String comment;
-
-  public CommentOnReviewNotification(Review review, User author, String comment) {
-    this.review = review;
-    this.author = author;
-    this.comment = comment;
-  }
-
-  public Review getReview() {
-    return review;
-  }
-
-  public User getAuthor() {
-    return author;
-  }
-
-  public String getComment() {
-    return comment;
-  }
-
-}
index 602f4e51791dae3b7afcb4e1424d97da662ee71b..e8200bd4b069dd11cfbe65ae748c03d715909059 100644 (file)
 package org.sonar.server.notifications.reviews;
 
 import org.sonar.api.ServerComponent;
+import org.sonar.api.database.model.Review;
 import org.sonar.api.database.model.User;
-import org.sonar.jpa.entity.Review;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationManager;
 import org.sonar.jpa.session.DatabaseSessionFactory;
-import org.sonar.server.notifications.NotificationManager;
 
 /**
  * @since 2.10
@@ -55,7 +56,12 @@ public class ReviewsNotificationManager implements ServerComponent {
   public void notifyCommentAdded(Long reviewId, Integer userId, String comment) {
     Review review = getReviewById(reviewId);
     User author = getUserById(userId);
-    CommentOnReviewNotification notification = new CommentOnReviewNotification(review, author, comment);
+
+    Notification notification = new Notification("review");
+    notification // FIXME include info about review
+        .setFieldValue("author", author.getLogin())
+        .setFieldValue("comment", comment);
+
     notificationManager.scheduleForSending(notification);
   }
 
index 49bcf2d6aa4bfe0500f1c3cf8f0ed62fb06af0e0..df234f0c490a9d5ee3102cae314bf1e69541e4fe 100644 (file)
@@ -40,6 +40,7 @@ import org.sonar.api.utils.TimeProfiler;
 import org.sonar.core.components.DefaultMetricFinder;
 import org.sonar.core.components.DefaultModelFinder;
 import org.sonar.core.components.DefaultRuleFinder;
+import org.sonar.core.notifications.DefaultNotificationManager;
 import org.sonar.jpa.dao.DaoFacade;
 import org.sonar.jpa.dao.MeasuresDao;
 import org.sonar.jpa.dao.ProfilesDao;
@@ -55,14 +56,7 @@ import org.sonar.server.database.EmbeddedDatabaseFactory;
 import org.sonar.server.database.JndiDatabaseConnector;
 import org.sonar.server.filters.FilterExecutor;
 import org.sonar.server.mavendeployer.MavenRepository;
-import org.sonar.server.notifications.NotificationManager;
-import org.sonar.server.notifications.NotificationQueue;
 import org.sonar.server.notifications.NotificationService;
-import org.sonar.server.notifications.email.EmailConfiguration;
-import org.sonar.server.notifications.email.EmailNotificationChannel;
-import org.sonar.server.notifications.reviews.CommentOnReviewAssignedToMe;
-import org.sonar.server.notifications.reviews.CommentOnReviewCreatedByMe;
-import org.sonar.server.notifications.reviews.CommentOnReviewEmailTemplate;
 import org.sonar.server.notifications.reviews.ReviewsNotificationManager;
 import org.sonar.server.plugins.*;
 import org.sonar.server.qualitymodel.DefaultModelManager;
@@ -193,16 +187,9 @@ public final class Platform {
     servicesContainer.as(Characteristics.CACHE).addComponent(JRubyI18n.class);
 
     // Notifications
-    servicesContainer.as(Characteristics.CACHE).addComponent(NotificationQueue.class);
     servicesContainer.as(Characteristics.CACHE).addComponent(NotificationService.class);
-    servicesContainer.as(Characteristics.CACHE).addComponent(NotificationManager.class);
+    servicesContainer.as(Characteristics.CACHE).addComponent(DefaultNotificationManager.class);
     servicesContainer.as(Characteristics.CACHE).addComponent(ReviewsNotificationManager.class);
-    // FIXME next five lines here just for tests:
-    servicesContainer.as(Characteristics.CACHE).addComponent(EmailConfiguration.class);
-    servicesContainer.as(Characteristics.CACHE).addComponent(EmailNotificationChannel.class);
-    servicesContainer.as(Characteristics.CACHE).addComponent(CommentOnReviewEmailTemplate.class);
-    servicesContainer.as(Characteristics.CACHE).addComponent(CommentOnReviewAssignedToMe.class);
-    servicesContainer.as(Characteristics.CACHE).addComponent(CommentOnReviewCreatedByMe.class);
 
     servicesContainer.start();
   }
index 77c3172f3b227202ac016bc0d778b1e45c68f6f7..163ece76c50250984cd24b18248161cf1c6fe816 100644 (file)
@@ -22,26 +22,24 @@ class EmailConfigurationController < ApplicationController
   SECTION=Navigation::SECTION_CONFIGURATION
   before_filter :admin_required
 
-  EmailConfiguration = org.sonar.server.notifications.email.EmailConfiguration
-
   def index
-    @smtp_host = Property.value(EmailConfiguration::SMTP_HOST, nil, EmailConfiguration::SMTP_HOST_DEFAULT)
-    @smtp_port = Property.value(EmailConfiguration::SMTP_PORT, nil, EmailConfiguration::SMTP_PORT_DEFAULT)
-    @smtp_use_tls = Property.value(EmailConfiguration::SMTP_USE_TLS, nil, EmailConfiguration::SMTP_USE_TLS_DEFAULT) == 'true'
-    @smtp_username = Property.value(EmailConfiguration::SMTP_USERNAME, nil, EmailConfiguration::SMTP_USERNAME_DEFAULT)
-    @smtp_password = Property.value(EmailConfiguration::SMTP_PASSWORD, nil, EmailConfiguration::SMTP_PASSWORD_DEFAULT)
-    @email_from = Property.value(EmailConfiguration::FROM, nil, EmailConfiguration::FROM_DEFAULT)
-    @email_prefix = Property.value(EmailConfiguration::PREFIX, nil, EmailConfiguration::PREFIX_DEFAULT)
+    @smtp_host = Property.value(configuration::SMTP_HOST, nil, configuration::SMTP_HOST_DEFAULT)
+    @smtp_port = Property.value(configuration::SMTP_PORT, nil, configuration::SMTP_PORT_DEFAULT)
+    @smtp_use_tls = Property.value(configuration::SMTP_USE_TLS, nil, configuration::SMTP_USE_TLS_DEFAULT) == 'true'
+    @smtp_username = Property.value(configuration::SMTP_USERNAME, nil, configuration::SMTP_USERNAME_DEFAULT)
+    @smtp_password = Property.value(configuration::SMTP_PASSWORD, nil, configuration::SMTP_PASSWORD_DEFAULT)
+    @email_from = Property.value(configuration::FROM, nil, configuration::FROM_DEFAULT)
+    @email_prefix = Property.value(configuration::PREFIX, nil, configuration::PREFIX_DEFAULT)
   end
 
   def save
-    Property.set(EmailConfiguration::SMTP_HOST, params[:smtp_host])
-    Property.set(EmailConfiguration::SMTP_PORT, params[:smtp_port])
-    Property.set(EmailConfiguration::SMTP_USE_TLS, params[:smtp_use_tls] == 'true')
-    Property.set(EmailConfiguration::SMTP_USERNAME, params[:smtp_username])
-    Property.set(EmailConfiguration::SMTP_PASSWORD, params[:smtp_password])
-    Property.set(EmailConfiguration::FROM, params[:email_from])
-    Property.set(EmailConfiguration::PREFIX, params[:email_prefix])
+    Property.set(configuration::SMTP_HOST, params[:smtp_host])
+    Property.set(configuration::SMTP_PORT, params[:smtp_port])
+    Property.set(configuration::SMTP_USE_TLS, params[:smtp_use_tls] == 'true')
+    Property.set(configuration::SMTP_USERNAME, params[:smtp_username])
+    Property.set(configuration::SMTP_PASSWORD, params[:smtp_password])
+    Property.set(configuration::FROM, params[:email_from])
+    Property.set(configuration::PREFIX, params[:email_prefix])
     redirect_to :action => 'index'
   end
 
@@ -53,7 +51,7 @@ class EmailConfigurationController < ApplicationController
       flash[:notice] = 'You must provide address where to send test email'
     else
       begin
-        java_facade.getCoreComponentByClassname('org.sonar.server.notifications.email.EmailNotificationChannel').sendTestEmail(to_address, subject, message)
+        java_facade.getComponentByClassname('email', 'org.sonar.plugins.email.EmailNotificationChannel').sendTestEmail(to_address, subject, message)
       rescue Exception => e
         flash[:error] = e.message
       end
@@ -61,4 +59,10 @@ class EmailConfigurationController < ApplicationController
     redirect_to :action => 'index'
   end
 
+  private
+
+  def configuration
+    java_facade.getComponentByClassname('email', 'org.sonar.plugins.email.EmailConfiguration').class
+  end
+
 end
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/215_create_notifications.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/215_create_notifications.rb
new file mode 100644 (file)
index 0000000..ed81964
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2011 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# Sonar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+#
+
+#
+# Sonar 2.10
+#
+class CreateNotifications < ActiveRecord::Migration
+
+  def self.up
+    create_table 'notifications' do |t|
+      t.column 'created_at', :datetime, :null => true
+      t.column 'data', :binary, :null => true
+    end
+  end
+
+end
diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/NotificationManagerTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/NotificationManagerTest.java
deleted file mode 100644 (file)
index b1a00de..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.*;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.io.Serializable;
-
-public class NotificationManagerTest {
-
-  private static Integer USER_SIMON = 1;
-  private static Integer USER_EVGENY = 2;
-
-  private NotificationChannel emailChannel;
-  private NotificationChannel gtalkChannel;
-
-  private NotificationDispatcher commentOnReviewAssignedToMe;
-  private Integer assignee;
-  private NotificationDispatcher commentOnReviewCreatedByMe;
-  private Integer creator;
-
-  private NotificationQueue queue;
-  private NotificationManager manager;
-
-  @Before
-  public void setUp() {
-    emailChannel = mock(NotificationChannel.class);
-    when(emailChannel.getKey()).thenReturn("email");
-    doAnswer(new Answer<Serializable>() {
-      public Serializable answer(InvocationOnMock invocation) throws Throwable {
-        return (Serializable) invocation.getArguments()[1];
-      }
-    }).when(emailChannel).createDataForPersistance(any(Notification.class), any(Integer.class));
-
-    gtalkChannel = mock(NotificationChannel.class);
-    when(gtalkChannel.getKey()).thenReturn("gtalk");
-    doAnswer(new Answer<Serializable>() {
-      public Serializable answer(InvocationOnMock invocation) throws Throwable {
-        return (Serializable) invocation.getArguments()[1];
-      }
-    }).when(gtalkChannel).createDataForPersistance(any(Notification.class), any(Integer.class));
-
-    commentOnReviewAssignedToMe = mock(NotificationDispatcher.class);
-    when(commentOnReviewAssignedToMe.getKey()).thenReturn("comment on review assigned to me");
-    doAnswer(new Answer<Object>() {
-      public Object answer(InvocationOnMock invocation) throws Throwable {
-        ((NotificationDispatcher.Context) invocation.getArguments()[1]).addUser(assignee);
-        return null;
-      }
-    }).when(commentOnReviewAssignedToMe).dispatch(any(Notification.class), any(NotificationDispatcher.Context.class));
-
-    commentOnReviewCreatedByMe = mock(NotificationDispatcher.class);
-    when(commentOnReviewCreatedByMe.getKey()).thenReturn("comment on review created by me");
-    doAnswer(new Answer<Object>() {
-      public Object answer(InvocationOnMock invocation) throws Throwable {
-        ((NotificationDispatcher.Context) invocation.getArguments()[1]).addUser(creator);
-        return null;
-      }
-    }).when(commentOnReviewCreatedByMe).dispatch(any(Notification.class), any(NotificationDispatcher.Context.class));
-
-    NotificationDispatcher[] dispatchers = new NotificationDispatcher[] { commentOnReviewAssignedToMe, commentOnReviewCreatedByMe };
-    NotificationChannel[] channels = new NotificationChannel[] { emailChannel, gtalkChannel };
-    queue = mock(NotificationQueue.class);
-    manager = spy(new NotificationManager(queue, dispatchers, channels));
-    doReturn(false).when(manager).isEnabled(any(Integer.class), any(NotificationChannel.class), any(NotificationDispatcher.class));
-  }
-
-  /**
-   * Given:
-   * Simon wants to receive notifications by email on comments for reviews assigned to him or created by him.
-   *
-   * When:
-   * Freddy adds comment to review created by Simon and assigned to Simon.
-   * 
-   * Then:
-   * Only one notification should be delivered to Simon by Email.
-   */
-  @Test
-  public void scenario1() {
-    doReturn(true).when(manager).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe);
-    doReturn(true).when(manager).isEnabled(USER_SIMON, emailChannel, commentOnReviewCreatedByMe);
-
-    Notification notification = mock(Notification.class);
-    creator = USER_SIMON;
-    assignee = USER_SIMON;
-
-    manager.scheduleForSending(notification);
-
-    verify(queue).add(USER_SIMON, emailChannel);
-    verifyNoMoreInteractions(queue);
-  }
-
-  /**
-   * Given:
-   * Evgeny wants to receive notification by GTalk on comments for reviews created by him.
-   * Simon wants to receive notification by Email on comments for reviews assigned to him.
-   * 
-   * When:
-   * Freddy adds comment to review created by Evgeny and assigned to Simon.
-   * 
-   * Then:
-   * Two notifications should be delivered - one to Simon by Email and another to Evgeny by GTalk.
-   */
-  @Test
-  public void scenario2() {
-    doReturn(true).when(manager).isEnabled(USER_EVGENY, gtalkChannel, commentOnReviewCreatedByMe);
-    doReturn(true).when(manager).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe);
-
-    Notification notification = mock(Notification.class);
-    creator = USER_EVGENY;
-    assignee = USER_SIMON;
-
-    manager.scheduleForSending(notification);
-
-    verify(queue).add(USER_EVGENY, gtalkChannel);
-    verify(queue).add(USER_SIMON, emailChannel);
-    verifyNoMoreInteractions(queue);
-  }
-
-  /**
-   * Given:
-   * Simon wants to receive notifications by Email and GTLak on comments for reviews assigned to him.
-   * 
-   * When:
-   * Freddy adds comment to review created by Evgeny and assigned to Simon.
-   * 
-   * Then:
-   * Two notifications should be delivered to Simon - one by Email and another by GTalk.
-   */
-  @Test
-  public void scenario3() {
-    doReturn(true).when(manager).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe);
-    doReturn(true).when(manager).isEnabled(USER_SIMON, gtalkChannel, commentOnReviewAssignedToMe);
-
-    Notification notification = mock(Notification.class);
-    creator = USER_EVGENY;
-    assignee = USER_SIMON;
-
-    manager.scheduleForSending(notification);
-
-    verify(queue).add(USER_SIMON, gtalkChannel);
-    verify(queue).add(USER_SIMON, emailChannel);
-    verifyNoMoreInteractions(queue);
-  }
-
-  /**
-   * Given:
-   * Nobody wants to receive notifications.
-   * 
-   * When:
-   * Freddy adds comment to review created by Evgeny and assigned to Simon.
-   * 
-   * Then:
-   * No notifications.
-   */
-  @Test
-  public void scenario4() {
-    Notification notification = mock(Notification.class);
-    creator = USER_EVGENY;
-    assignee = USER_SIMON;
-
-    manager.scheduleForSending(notification);
-
-    verifyNoMoreInteractions(queue);
-  }
-
-}
index 7c1c0b036152f9ef74a491c9f9e0d9e96ea7f053..46633e27a906f73e62224e781fd99b9c8cdae88c 100644 (file)
  */
 package org.sonar.server.notifications;
 
+import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.*;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.api.notifications.NotificationDispatcher;
+import org.sonar.core.notifications.DefaultNotificationManager;
+import org.sonar.jpa.entity.NotificationQueueElement;
 
 public class NotificationServiceTest {
 
-  private NotificationQueue queue;
+  private static String USER_SIMON = "simon";
+  private static String USER_EVGENY = "evgeny";
+
+  private NotificationChannel emailChannel;
+  private NotificationChannel gtalkChannel;
+
+  private NotificationDispatcher commentOnReviewAssignedToMe;
+  private String assignee;
+  private NotificationDispatcher commentOnReviewCreatedByMe;
+  private String creator;
+
+  private DefaultNotificationManager manager;
   private NotificationService service;
 
   @Before
   public void setUp() {
-    queue = mock(NotificationQueue.class);
-    service = spy(new NotificationService(queue));
-    service.setPeriod(10);
+    emailChannel = mock(NotificationChannel.class);
+    when(emailChannel.getKey()).thenReturn("email");
+
+    gtalkChannel = mock(NotificationChannel.class);
+    when(gtalkChannel.getKey()).thenReturn("gtalk");
+
+    commentOnReviewAssignedToMe = mock(NotificationDispatcher.class);
+    when(commentOnReviewAssignedToMe.getKey()).thenReturn("comment on review assigned to me");
+    doAnswer(new Answer<Object>() {
+      public Object answer(InvocationOnMock invocation) throws Throwable {
+        ((NotificationDispatcher.Context) invocation.getArguments()[1]).addUser(assignee);
+        return null;
+      }
+    }).when(commentOnReviewAssignedToMe).dispatch(any(Notification.class), any(NotificationDispatcher.Context.class));
+
+    commentOnReviewCreatedByMe = mock(NotificationDispatcher.class);
+    when(commentOnReviewCreatedByMe.getKey()).thenReturn("comment on review created by me");
+    doAnswer(new Answer<Object>() {
+      public Object answer(InvocationOnMock invocation) throws Throwable {
+        ((NotificationDispatcher.Context) invocation.getArguments()[1]).addUser(creator);
+        return null;
+      }
+    }).when(commentOnReviewCreatedByMe).dispatch(any(Notification.class), any(NotificationDispatcher.Context.class));
+
+    NotificationDispatcher[] dispatchers = new NotificationDispatcher[] { commentOnReviewAssignedToMe, commentOnReviewCreatedByMe };
+    NotificationChannel[] channels = new NotificationChannel[] { emailChannel, gtalkChannel };
+    manager = mock(DefaultNotificationManager.class);
+    service = spy(new NotificationService(manager, dispatchers, channels));
+    doReturn(false).when(service).isEnabled(any(String.class), any(NotificationChannel.class), any(NotificationDispatcher.class));
   }
 
   @Test
   public void shouldPeriodicallyProcessQueue() throws Exception {
-    service.start();
+    NotificationQueueElement queueElement = mock(NotificationQueueElement.class);
+    Notification notification = mock(Notification.class);
+    when(queueElement.getNotification()).thenReturn(notification);
+    when(manager.getFromQueue()).thenReturn(queueElement).thenReturn(null);
+    doNothing().when(service).deliver(any(Notification.class));
 
-    NotificationQueue.Element element = mock(NotificationQueue.Element.class);
-    when(queue.get()).thenReturn(element);
+    service.setPeriod(10);
+    service.start();
     Thread.sleep(50);
+    service.stop();
 
-    verify(service, atLeastOnce()).deliver(element);
+    verify(service).deliver(notification);
+  }
 
-    service.stop();
+  /**
+   * Given:
+   * Simon wants to receive notifications by email on comments for reviews assigned to him or created by him.
+   *
+   * When:
+   * Freddy adds comment to review created by Simon and assigned to Simon.
+   * 
+   * Then:
+   * Only one notification should be delivered to Simon by Email.
+   */
+  @Test
+  public void scenario1() {
+    doReturn(true).when(service).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe);
+    doReturn(true).when(service).isEnabled(USER_SIMON, emailChannel, commentOnReviewCreatedByMe);
+
+    Notification notification = mock(Notification.class);
+    creator = USER_SIMON;
+    assignee = USER_SIMON;
+
+    service.deliver(notification);
+
+    verify(emailChannel).deliver(notification, USER_SIMON);
+    verifyNoMoreInteractions(emailChannel);
+    verifyNoMoreInteractions(gtalkChannel);
   }
+
+  /**
+   * Given:
+   * Evgeny wants to receive notification by GTalk on comments for reviews created by him.
+   * Simon wants to receive notification by Email on comments for reviews assigned to him.
+   * 
+   * When:
+   * Freddy adds comment to review created by Evgeny and assigned to Simon.
+   * 
+   * Then:
+   * Two notifications should be delivered - one to Simon by Email and another to Evgeny by GTalk.
+   */
+  @Test
+  public void scenario2() {
+    doReturn(true).when(service).isEnabled(USER_EVGENY, gtalkChannel, commentOnReviewCreatedByMe);
+    doReturn(true).when(service).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe);
+
+    Notification notification = mock(Notification.class);
+    creator = USER_EVGENY;
+    assignee = USER_SIMON;
+
+    service.deliver(notification);
+
+    verify(emailChannel).deliver(notification, USER_SIMON);
+    verify(gtalkChannel).deliver(notification, USER_EVGENY);
+    verifyNoMoreInteractions(emailChannel);
+    verifyNoMoreInteractions(gtalkChannel);
+  }
+
+  /**
+   * Given:
+   * Simon wants to receive notifications by Email and GTLak on comments for reviews assigned to him.
+   * 
+   * When:
+   * Freddy adds comment to review created by Evgeny and assigned to Simon.
+   * 
+   * Then:
+   * Two notifications should be delivered to Simon - one by Email and another by GTalk.
+   */
+  @Test
+  public void scenario3() {
+    doReturn(true).when(service).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe);
+    doReturn(true).when(service).isEnabled(USER_SIMON, gtalkChannel, commentOnReviewAssignedToMe);
+
+    Notification notification = mock(Notification.class);
+    creator = USER_EVGENY;
+    assignee = USER_SIMON;
+
+    service.deliver(notification);
+
+    verify(emailChannel).deliver(notification, USER_SIMON);
+    verify(gtalkChannel).deliver(notification, USER_SIMON);
+    verifyNoMoreInteractions(emailChannel);
+    verifyNoMoreInteractions(gtalkChannel);
+  }
+
+  /**
+   * Given:
+   * Nobody wants to receive notifications.
+   * 
+   * When:
+   * Freddy adds comment to review created by Evgeny and assigned to Simon.
+   * 
+   * Then:
+   * No notifications.
+   */
+  @Test
+  public void scenario4() {
+    Notification notification = mock(Notification.class);
+    creator = USER_EVGENY;
+    assignee = USER_SIMON;
+
+    service.deliver(notification);
+
+    verifyNoMoreInteractions(emailChannel);
+    verifyNoMoreInteractions(gtalkChannel);
+  }
+
 }
diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java
deleted file mode 100644 (file)
index 0b51f3e..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications.email;
-
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.nullValue;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.dumbster.smtp.SimpleSmtpServer;
-import com.dumbster.smtp.SmtpMessage;
-import org.apache.commons.mail.EmailException;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.net.ServerSocket;
-
-public class EmailNotificationChannelTest {
-
-  private static int port;
-
-  private SimpleSmtpServer server;
-
-  private EmailConfiguration configuration;
-  private EmailNotificationChannel channel;
-
-  @BeforeClass
-  public static void selectPort() {
-    port = getNextAvailablePort();
-  }
-
-  private static int getNextAvailablePort() {
-    try {
-      ServerSocket socket = new ServerSocket(0);
-      int unusedPort = socket.getLocalPort();
-      socket.close();
-      return unusedPort;
-    } catch (IOException e) {
-      throw new RuntimeException("Error getting an available port from system", e);
-    }
-  }
-
-  @Before
-  public void setUp() {
-    server = SimpleSmtpServer.start(port);
-    configuration = mock(EmailConfiguration.class);
-    channel = new EmailNotificationChannel(configuration, null, null);
-  }
-
-  @After
-  public void tearDown() {
-    if (!server.isStopped()) {
-      server.stop();
-    }
-  }
-
-  @Test
-  public void shouldSendTestEmail() throws Exception {
-    configure();
-    channel.sendTestEmail("user@nowhere", "Test Message from Sonar", "This is a test message from Sonar.");
-
-    assertThat(server.getReceivedEmailSize(), is(1));
-    SmtpMessage email = (SmtpMessage) server.getReceivedEmail().next();
-
-    assertThat(email.getHeaderValue("From"), is("Sonar <server@nowhere>"));
-    assertThat(email.getHeaderValue("To"), is("<user@nowhere>"));
-    assertThat(email.getHeaderValue("Subject"), is("[SONAR] Test Message from Sonar"));
-    assertThat(email.getBody(), is("This is a test message from Sonar."));
-  }
-
-  @Test(expected = EmailException.class)
-  public void shouldThrowAnExceptionWhenUnableToSendTestEmail() throws Exception {
-    configure();
-    server.stop();
-
-    channel.sendTestEmail("user@nowhere", "Test Message from Sonar", "This is a test message from Sonar.");
-  }
-
-  @Test
-  public void shouldNotSendEmailWhenHostnameNotConfigured() throws Exception {
-    EmailMessage emailMessage = new EmailMessage()
-        .setTo("user@nowhere")
-        .setSubject("Foo")
-        .setMessage("Bar");
-    channel.deliver(emailMessage);
-    assertThat(server.getReceivedEmailSize(), is(0));
-  }
-
-  @Test
-  public void shouldSendThreadedEmail() throws Exception {
-    configure();
-    EmailMessage emailMessage = new EmailMessage()
-        .setMessageId("reviews/view/1")
-        .setFrom("Full Username")
-        .setTo("user@nowhere")
-        .setSubject("Review #3")
-        .setMessage("I'll take care of this violation.");
-    channel.deliver(emailMessage);
-
-    assertThat(server.getReceivedEmailSize(), is(1));
-    SmtpMessage email = (SmtpMessage) server.getReceivedEmail().next();
-
-    assertThat(email.getHeaderValue("In-Reply-To"), is("<reviews/view/1@nemo.sonarsource.org>"));
-    assertThat(email.getHeaderValue("References"), is("<reviews/view/1@nemo.sonarsource.org>"));
-
-    assertThat(email.getHeaderValue("List-Id"), is("<sonar.nemo.sonarsource.org>"));
-    assertThat(email.getHeaderValue("List-Archive"), is("http://nemo.sonarsource.org"));
-
-    assertThat(email.getHeaderValue("From"), is("Full Username <server@nowhere>"));
-    assertThat(email.getHeaderValue("To"), is("<user@nowhere>"));
-    assertThat(email.getHeaderValue("Subject"), is("[SONAR] Review #3"));
-    assertThat(email.getBody(), is("I'll take care of this violation."));
-  }
-
-  @Test
-  public void shouldSendNonThreadedEmail() {
-    configure();
-    EmailMessage emailMessage = new EmailMessage()
-        .setTo("user@nowhere")
-        .setSubject("Foo")
-        .setMessage("Bar");
-    channel.deliver(emailMessage);
-
-    assertThat(server.getReceivedEmailSize(), is(1));
-    SmtpMessage email = (SmtpMessage) server.getReceivedEmail().next();
-
-    assertThat(email.getHeaderValue("In-Reply-To"), nullValue());
-    assertThat(email.getHeaderValue("References"), nullValue());
-
-    assertThat(email.getHeaderValue("List-Id"), is("<sonar.nemo.sonarsource.org>"));
-    assertThat(email.getHeaderValue("List-Archive"), is("http://nemo.sonarsource.org"));
-
-    assertThat(email.getHeaderValue("From"), is("Sonar <server@nowhere>"));
-    assertThat(email.getHeaderValue("To"), is("<user@nowhere>"));
-    assertThat(email.getHeaderValue("Subject"), is("[SONAR] Foo"));
-    assertThat(email.getBody(), is("Bar"));
-  }
-
-  @Test
-  public void shouldNotThrowAnExceptionWhenUnableToSendEmail() {
-    configure();
-    server.stop();
-
-    EmailMessage emailMessage = new EmailMessage()
-        .setTo("user@nowhere")
-        .setSubject("Foo")
-        .setMessage("Bar");
-    channel.deliver(emailMessage);
-  }
-
-  private void configure() {
-    when(configuration.getSmtpHost()).thenReturn("localhost");
-    when(configuration.getSmtpPort()).thenReturn(Integer.toString(port));
-    when(configuration.getFrom()).thenReturn("server@nowhere");
-    when(configuration.getPrefix()).thenReturn("[SONAR]");
-  }
-
-}
diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMeTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMeTest.java
deleted file mode 100644 (file)
index 62c375c..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications.reviews;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.database.model.User;
-import org.sonar.jpa.entity.Review;
-import org.sonar.server.notifications.NotificationDispatcher;
-
-public class CommentOnReviewAssignedToMeTest {
-
-  private NotificationDispatcher.Context context;
-  private CommentOnReviewAssignedToMe dispatcher;
-
-  @Before
-  public void setUp() {
-    context = mock(NotificationDispatcher.Context.class);
-    dispatcher = new CommentOnReviewAssignedToMe();
-  }
-
-  @Test
-  public void shouldDispatchToAssignee() {
-    CommentOnReviewNotification notification = new CommentOnReviewNotification(new Review().setAssigneeId(1), new User(), "comment");
-    dispatcher.dispatch(notification, context);
-    verify(context).addUser(1);
-
-    notification = new CommentOnReviewNotification(new Review().setAssigneeId(2), new User(), "comment");
-    dispatcher.dispatch(notification, context);
-    verify(context).addUser(2);
-  }
-
-  @Test
-  public void shouldNotDispatchWhenNotAssigned() {
-    CommentOnReviewNotification notification = new CommentOnReviewNotification(new Review(), new User(), "comment");
-    dispatcher.dispatch(notification, context);
-    verifyNoMoreInteractions(context);
-  }
-
-}
diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMeTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMeTest.java
deleted file mode 100644 (file)
index 4f1c176..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.server.notifications.reviews;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.database.model.User;
-import org.sonar.jpa.entity.Review;
-import org.sonar.server.notifications.NotificationDispatcher;
-
-public class CommentOnReviewCreatedByMeTest {
-
-  private NotificationDispatcher.Context context;
-  private CommentOnReviewCreatedByMe dispatcher;
-
-  @Before
-  public void setUp() {
-    context = mock(NotificationDispatcher.Context.class);
-    dispatcher = new CommentOnReviewCreatedByMe();
-  }
-
-  @Test
-  public void shouldDispatchToCreator() {
-    CommentOnReviewNotification notification = new CommentOnReviewNotification(new Review().setUserId(1), new User(), "comment");
-    dispatcher.dispatch(notification, context);
-    verify(context).addUser(1);
-
-    notification = new CommentOnReviewNotification(new Review().setUserId(2), new User(), "comment");
-    dispatcher.dispatch(notification, context);
-    verify(context).addUser(2);
-  }
-
-}
index 3dd2e3e6f3bf71927e0a53f9fde4207c9987e770..8e0eaba69538c7767f2d43b708e685cbab145947 100644 (file)
@@ -24,8 +24,8 @@ import static org.junit.Assert.assertThat;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.sonar.api.database.model.Review;
 import org.sonar.api.database.model.User;
-import org.sonar.jpa.entity.Review;
 import org.sonar.jpa.test.AbstractDbUnitTestCase;
 
 public class ReviewsNotificationManagerTest extends AbstractDbUnitTestCase {