aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2013-08-26 17:25:48 +0200
committerJulien HENRY <julien.henry@sonarsource.com>2013-08-26 17:25:48 +0200
commit8d4325511265cae0947a763cf559b2514052e4b7 (patch)
tree6a1d51fd446de81470dafc5a2b78530cdbd1bdd9
parent7d8aa2afd1c693fa4cdf94c1208ec130e8aa4b94 (diff)
downloadsonarqube-8d4325511265cae0947a763cf559b2514052e4b7.tar.gz
sonarqube-8d4325511265cae0947a763cf559b2514052e4b7.zip
SONAR-4524 Migrated Notifications to MyBatis
-rw-r--r--sonar-core/src/main/java/org/sonar/core/notification/DefaultNotificationManager.java44
-rw-r--r--sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDao.java75
-rw-r--r--sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDto.java (renamed from sonar-core/src/main/java/org/sonar/core/notification/NotificationQueueElement.java)80
-rw-r--r--sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueMapper.java36
-rw-r--r--sonar-core/src/main/java/org/sonar/core/notification/db/package-info.java23
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java3
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java51
-rw-r--r--sonar-core/src/main/resources/META-INF/persistence.xml3
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/notification/db/NotificationQueueMapper.xml40
-rw-r--r--sonar-core/src/test/java/org/sonar/core/notification/DefaultNotificationManagerTest.java38
-rw-r--r--sonar-core/src/test/java/org/sonar/core/notification/db/NotificationQueueDaoTest.java75
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_delete_notification-result.xml13
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_delete_notification.xml23
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_findOldest.xml23
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_insert_new_notification_queue-result.xml8
-rw-r--r--sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java9
-rw-r--r--sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java12
17 files changed, 473 insertions, 83 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/notification/DefaultNotificationManager.java b/sonar-core/src/main/java/org/sonar/core/notification/DefaultNotificationManager.java
index 0a344a83a07..6d8b17921a2 100644
--- a/sonar-core/src/main/java/org/sonar/core/notification/DefaultNotificationManager.java
+++ b/sonar-core/src/main/java/org/sonar/core/notification/DefaultNotificationManager.java
@@ -23,17 +23,17 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.SetMultimap;
-import org.sonar.api.database.DatabaseSession;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
import org.sonar.api.notifications.NotificationDispatcher;
import org.sonar.api.notifications.NotificationManager;
+import org.sonar.core.notification.db.NotificationQueueDao;
+import org.sonar.core.notification.db.NotificationQueueDto;
import org.sonar.core.properties.PropertiesDao;
-import org.sonar.jpa.session.DatabaseSessionFactory;
import javax.annotation.Nullable;
+
import java.util.Arrays;
-import java.util.Date;
import java.util.List;
/**
@@ -42,56 +42,46 @@ import java.util.List;
public class DefaultNotificationManager implements NotificationManager {
private NotificationChannel[] notificationChannels;
- private DatabaseSessionFactory sessionFactory;
+ private NotificationQueueDao notificationQueueDao;
private PropertiesDao propertiesDao;
/**
* Default constructor used by Pico
*/
- public DefaultNotificationManager(NotificationChannel[] channels, DatabaseSessionFactory sessionFactory, PropertiesDao propertiesDao) {
+ public DefaultNotificationManager(NotificationChannel[] channels, NotificationQueueDao notificationQueueDao, PropertiesDao propertiesDao) {
this.notificationChannels = channels;
- this.sessionFactory = sessionFactory;
+ this.notificationQueueDao = notificationQueueDao;
this.propertiesDao = propertiesDao;
}
/**
* Constructor if no notification channel
*/
- public DefaultNotificationManager(DatabaseSessionFactory sessionFactory, PropertiesDao propertiesDao) {
- this(new NotificationChannel[0], sessionFactory, propertiesDao);
+ public DefaultNotificationManager(NotificationQueueDao notificationQueueDao, PropertiesDao propertiesDao) {
+ this(new NotificationChannel[0], notificationQueueDao, propertiesDao);
}
/**
* {@inheritDoc}
*/
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();
+ NotificationQueueDto dto = NotificationQueueDto.toNotificationQueueDto(notification);
+ notificationQueueDao.insert(dto);
}
/**
* Give the notification queue so that it can be processed
*/
- public NotificationQueueElement getFromQueue() {
- DatabaseSession session = sessionFactory.getSession();
- String hql = "FROM " + NotificationQueueElement.class.getSimpleName() + " ORDER BY createdAt ASC";
- List<NotificationQueueElement> notifications = session.createQuery(hql).setMaxResults(1).getResultList();
+ public Notification getFromQueue() {
+ int batchSize = 1;
+ List<NotificationQueueDto> notifications = notificationQueueDao.findOldest(batchSize);
if (notifications.isEmpty()) {
- // UGLY - waiting for a clean way to manage JDBC connections without Hibernate - myBatis is coming soon
- // This code is highly coupled to org.sonar.server.notifications.NotificationService, which periodically executes
- // several times the methods getFromQueue() and isEnabled(). The session is closed only at the end of the task -
- // when there are no more notifications to process - to ensure "better" performances.
- sessionFactory.clear();
return null;
}
- NotificationQueueElement notification = notifications.get(0);
- session.removeWithoutFlush(notification);
- session.commit();
- return notification;
+ notificationQueueDao.delete(notifications);
+
+ // If batchSize is increased then we should return a list instead of a single element
+ return notifications.get(0).toNotification();
}
diff --git a/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDao.java b/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDao.java
new file mode 100644
index 00000000000..8bb11e157d8
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDao.java
@@ -0,0 +1,75 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.core.notification.db;
+
+import org.apache.ibatis.session.SqlSession;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+import org.sonar.core.persistence.MyBatis;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @since 4.0
+ */
+public class NotificationQueueDao implements BatchComponent, ServerComponent {
+
+ private final MyBatis mybatis;
+
+ public NotificationQueueDao(MyBatis mybatis) {
+ this.mybatis = mybatis;
+ }
+
+ public void insert(NotificationQueueDto dto) {
+ SqlSession session = mybatis.openSession();
+ try {
+ session.getMapper(NotificationQueueMapper.class).insert(dto);
+ session.commit();
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ public void delete(List<NotificationQueueDto> dtos) {
+ SqlSession session = mybatis.openBatchSession();
+ try {
+ for (NotificationQueueDto dto : dtos) {
+ session.getMapper(NotificationQueueMapper.class).delete(dto.getId());
+ }
+ session.commit();
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ public List<NotificationQueueDto> findOldest(int count) {
+ if (count < 1) {
+ return Collections.emptyList();
+ }
+ SqlSession session = mybatis.openSession();
+ try {
+ return session.getMapper(NotificationQueueMapper.class).findOldest(count);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/notification/NotificationQueueElement.java b/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDto.java
index fcc90f8ca79..4e9668a507b 100644
--- a/sonar-core/src/main/java/org/sonar/core/notification/NotificationQueueElement.java
+++ b/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueDto.java
@@ -17,56 +17,88 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.core.notification;
+
+package org.sonar.core.notification.db;
import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.sonar.api.notifications.Notification;
import org.sonar.api.utils.SonarException;
-import javax.persistence.*;
-import java.io.*;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.util.Date;
-@Entity
-@Table(name = "notifications")
-public class NotificationQueueElement {
-
- @Id
- @Column(name = "id")
- @GeneratedValue
- private Integer id;
+/**
+ * @since 4.0
+ */
+public class NotificationQueueDto {
- @Column(name = "created_at")
+ private Long id;
private Date createdAt;
-
- @Column(name = "data", updatable = true, nullable = true, length = 167772150)
private byte[] data;
- public Integer getId() {
+ public Long getId() {
return id;
}
- public void setId(Integer id) {
+ public NotificationQueueDto setId(Long id) {
this.id = id;
+ return this;
}
public Date getCreatedAt() {
return createdAt;
}
- public void setCreatedAt(Date createdAt) {
+ public NotificationQueueDto setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
+ return this;
}
- public void setNotification(Notification notification) {
+ public byte[] getData() {
+ return data;
+ }
+
+ public NotificationQueueDto setData(byte[] data) {
+ this.data = data;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ NotificationQueueDto actionPlanDto = (NotificationQueueDto) o;
+ return !(id != null ? !id.equals(actionPlanDto.id) : actionPlanDto.id != null);
+ }
+
+ @Override
+ public int hashCode() {
+ return id != null ? id.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
+ }
+
+ public static NotificationQueueDto toNotificationQueueDto(Notification notification) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(notification);
objectOutputStream.close();
- this.data = byteArrayOutputStream.toByteArray();
+ return new NotificationQueueDto().setData(byteArrayOutputStream.toByteArray());
} catch (IOException e) {
throw new SonarException(e);
@@ -76,7 +108,7 @@ public class NotificationQueueElement {
}
}
- public Notification getNotification() {
+ public Notification toNotification() {
if (this.data == null) {
return null;
}
@@ -93,14 +125,10 @@ public class NotificationQueueElement {
} catch (ClassNotFoundException e) {
throw new SonarException(e);
-
+
} finally {
IOUtils.closeQuietly(byteArrayInputStream);
}
}
- @Override
- public String toString() {
- return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString();
- }
}
diff --git a/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueMapper.java b/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueMapper.java
new file mode 100644
index 00000000000..120637c6e98
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/notification/db/NotificationQueueMapper.java
@@ -0,0 +1,36 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.core.notification.db;
+
+import java.util.List;
+
+/**
+ * @since 4.0
+ */
+public interface NotificationQueueMapper {
+
+ void insert(NotificationQueueDto actionPlanDto);
+
+ void delete(Long id);
+
+ List<NotificationQueueDto> findOldest(int count);
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/notification/db/package-info.java b/sonar-core/src/main/java/org/sonar/core/notification/db/package-info.java
new file mode 100644
index 00000000000..f33c44e5652
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/notification/db/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.core.notification.db;
+
+import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
index 3027aada8fa..9512388b61a 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
@@ -19,6 +19,8 @@
*/
package org.sonar.core.persistence;
+import org.sonar.core.notification.db.NotificationQueueDao;
+
import com.google.common.collect.ImmutableList;
import org.sonar.core.dashboard.ActiveDashboardDao;
import org.sonar.core.dashboard.DashboardDao;
@@ -65,6 +67,7 @@ public final class DaoUtils {
IssueFilterFavouriteDao.class,
LoadedTemplateDao.class,
MeasureFilterDao.class,
+ NotificationQueueDao.class,
PermissionDao.class,
PropertiesDao.class,
PurgeDao.class,
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
index 19a9ecbbf57..ced5b2e5eef 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
@@ -24,7 +24,11 @@ import com.google.common.io.Closeables;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.Environment;
-import org.apache.ibatis.session.*;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.ibatis.type.JdbcType;
import org.slf4j.LoggerFactory;
@@ -35,7 +39,14 @@ import org.sonar.api.database.model.MeasureData;
import org.sonar.api.database.model.MeasureMapper;
import org.sonar.api.database.model.MeasureModel;
import org.sonar.core.config.Logback;
-import org.sonar.core.dashboard.*;
+import org.sonar.core.dashboard.ActiveDashboardDto;
+import org.sonar.core.dashboard.ActiveDashboardMapper;
+import org.sonar.core.dashboard.DashboardDto;
+import org.sonar.core.dashboard.DashboardMapper;
+import org.sonar.core.dashboard.WidgetDto;
+import org.sonar.core.dashboard.WidgetMapper;
+import org.sonar.core.dashboard.WidgetPropertyDto;
+import org.sonar.core.dashboard.WidgetPropertyMapper;
import org.sonar.core.dependency.DependencyDto;
import org.sonar.core.dependency.DependencyMapper;
import org.sonar.core.dependency.ResourceSnapshotDto;
@@ -44,9 +55,23 @@ import org.sonar.core.duplication.DuplicationMapper;
import org.sonar.core.duplication.DuplicationUnitDto;
import org.sonar.core.graph.jdbc.GraphDto;
import org.sonar.core.graph.jdbc.GraphDtoMapper;
-import org.sonar.core.issue.db.*;
+import org.sonar.core.issue.db.ActionPlanDto;
+import org.sonar.core.issue.db.ActionPlanMapper;
+import org.sonar.core.issue.db.ActionPlanStatsDto;
+import org.sonar.core.issue.db.ActionPlanStatsMapper;
+import org.sonar.core.issue.db.IssueChangeDto;
+import org.sonar.core.issue.db.IssueChangeMapper;
+import org.sonar.core.issue.db.IssueDto;
+import org.sonar.core.issue.db.IssueFilterDto;
+import org.sonar.core.issue.db.IssueFilterFavouriteDto;
+import org.sonar.core.issue.db.IssueFilterFavouriteMapper;
+import org.sonar.core.issue.db.IssueFilterMapper;
+import org.sonar.core.issue.db.IssueMapper;
+import org.sonar.core.issue.db.IssueStatsMapper;
import org.sonar.core.measure.MeasureFilterDto;
import org.sonar.core.measure.MeasureFilterMapper;
+import org.sonar.core.notification.db.NotificationQueueDto;
+import org.sonar.core.notification.db.NotificationQueueMapper;
import org.sonar.core.permission.PermissionTemplateDto;
import org.sonar.core.permission.PermissionTemplateGroupDto;
import org.sonar.core.permission.PermissionTemplateMapper;
@@ -55,7 +80,12 @@ import org.sonar.core.properties.PropertiesMapper;
import org.sonar.core.properties.PropertyDto;
import org.sonar.core.purge.PurgeMapper;
import org.sonar.core.purge.PurgeableSnapshotDto;
-import org.sonar.core.resource.*;
+import org.sonar.core.resource.ResourceDto;
+import org.sonar.core.resource.ResourceIndexDto;
+import org.sonar.core.resource.ResourceIndexerMapper;
+import org.sonar.core.resource.ResourceKeyUpdaterMapper;
+import org.sonar.core.resource.ResourceMapper;
+import org.sonar.core.resource.SnapshotDto;
import org.sonar.core.rule.RuleDto;
import org.sonar.core.rule.RuleMapper;
import org.sonar.core.source.jdbc.SnapshotDataDto;
@@ -63,7 +93,14 @@ import org.sonar.core.source.jdbc.SnapshotDataMapper;
import org.sonar.core.source.jdbc.SnapshotSourceMapper;
import org.sonar.core.template.LoadedTemplateDto;
import org.sonar.core.template.LoadedTemplateMapper;
-import org.sonar.core.user.*;
+import org.sonar.core.user.AuthorDto;
+import org.sonar.core.user.AuthorMapper;
+import org.sonar.core.user.GroupDto;
+import org.sonar.core.user.GroupRoleDto;
+import org.sonar.core.user.RoleMapper;
+import org.sonar.core.user.UserDto;
+import org.sonar.core.user.UserMapper;
+import org.sonar.core.user.UserRoleDto;
import java.io.InputStream;
@@ -102,6 +139,7 @@ public class MyBatis implements BatchComponent, ServerComponent {
loadAlias(conf, "GroupRole", GroupRoleDto.class);
loadAlias(conf, "LoadedTemplate", LoadedTemplateDto.class);
loadAlias(conf, "MeasureFilter", MeasureFilterDto.class);
+ loadAlias(conf, "NotificationQueue", NotificationQueueDto.class);
loadAlias(conf, "Property", PropertyDto.class);
loadAlias(conf, "PurgeableSnapshot", PurgeableSnapshotDto.class);
loadAlias(conf, "Resource", ResourceDto.class);
@@ -139,7 +177,8 @@ public class MyBatis implements BatchComponent, ServerComponent {
LoadedTemplateMapper.class, MeasureFilterMapper.class, PermissionTemplateMapper.class, PropertiesMapper.class, PurgeMapper.class,
ResourceKeyUpdaterMapper.class, ResourceIndexerMapper.class, ResourceSnapshotMapper.class, RoleMapper.class, RuleMapper.class,
SchemaMigrationMapper.class, SemaphoreMapper.class, UserMapper.class, WidgetMapper.class, WidgetPropertyMapper.class,
- MeasureMapper.class, SnapshotDataMapper.class, SnapshotSourceMapper.class, ActionPlanMapper.class, ActionPlanStatsMapper.class
+ MeasureMapper.class, SnapshotDataMapper.class, SnapshotSourceMapper.class, ActionPlanMapper.class, ActionPlanStatsMapper.class,
+ NotificationQueueMapper.class
};
loadMappers(conf, mappers);
configureLogback(mappers);
diff --git a/sonar-core/src/main/resources/META-INF/persistence.xml b/sonar-core/src/main/resources/META-INF/persistence.xml
index ffe8c102dd7..0cf0c2f31c8 100644
--- a/sonar-core/src/main/resources/META-INF/persistence.xml
+++ b/sonar-core/src/main/resources/META-INF/persistence.xml
@@ -30,7 +30,6 @@
<class>org.sonar.api.profiles.Alert</class>
<class>org.sonar.api.rules.ActiveRuleChange</class>
<class>org.sonar.api.rules.ActiveRuleParamChange</class>
- <class>org.sonar.core.notification.NotificationQueueElement</class>
<properties>
<property name="hibernate.current_session_context_class" value="thread"/>
@@ -46,4 +45,4 @@
<property name="hibernate.cache.use_query_cache" value="false"/>
</properties>
</persistence-unit>
-</persistence> \ No newline at end of file
+</persistence>
diff --git a/sonar-core/src/main/resources/org/sonar/core/notification/db/NotificationQueueMapper.xml b/sonar-core/src/main/resources/org/sonar/core/notification/db/NotificationQueueMapper.xml
new file mode 100644
index 00000000000..c14850598b2
--- /dev/null
+++ b/sonar-core/src/main/resources/org/sonar/core/notification/db/NotificationQueueMapper.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mappei.dtd">
+
+<mapper namespace="org.sonar.core.notification.db.NotificationQueueMapper">
+
+ <insert id="insert" parameterType="NotificationQueue" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
+ INSERT INTO notifications (created_at, data)
+ VALUES (current_timestamp, #{data})
+ </insert>
+
+ <delete id="delete" parameterType="long">
+ delete from notifications where id=#{id}
+ </delete>
+
+ <select id="findOldest" parameterType="int" resultType="NotificationQueue">
+ select id, created_at, data
+ from notifications
+ order by created_at asc
+ limit #{count}
+ </select>
+
+ <!-- SQL Server -->
+ <select id="findOldest" parameterType="int" resultType="NotificationQueue" databaseId="mssql">
+ select top (#{count}) id, created_at, data
+ from notifications
+ order by created_at asc
+ </select>
+
+ <!-- Oracle -->
+ <select id="findOldest" parameterType="int" resultType="NotificationQueue" databaseId="oracle">
+ select * from (select
+ id, created_at, data
+ from notifications
+ order by created_at asc
+ )
+ where rownum &lt;= #{count}
+ </select>
+
+</mapper>
diff --git a/sonar-core/src/test/java/org/sonar/core/notification/DefaultNotificationManagerTest.java b/sonar-core/src/test/java/org/sonar/core/notification/DefaultNotificationManagerTest.java
index 7953acd55aa..bdd0a264d4d 100644
--- a/sonar-core/src/test/java/org/sonar/core/notification/DefaultNotificationManagerTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/notification/DefaultNotificationManagerTest.java
@@ -23,21 +23,27 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
import org.sonar.api.notifications.NotificationDispatcher;
+import org.sonar.core.notification.db.NotificationQueueDao;
+import org.sonar.core.notification.db.NotificationQueueDto;
import org.sonar.core.properties.PropertiesDao;
import org.sonar.jpa.test.AbstractDbUnitTestCase;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
import static org.fest.assertions.Assertions.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.nullValue;
-import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.only;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class DefaultNotificationManagerTest extends AbstractDbUnitTestCase {
@@ -56,6 +62,9 @@ public class DefaultNotificationManagerTest extends AbstractDbUnitTestCase {
@Mock
private NotificationChannel twitterChannel;
+ @Mock
+ private NotificationQueueDao notificationQueueDao;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -63,14 +72,14 @@ public class DefaultNotificationManagerTest extends AbstractDbUnitTestCase {
when(emailChannel.getKey()).thenReturn("Email");
when(twitterChannel.getKey()).thenReturn("Twitter");
- manager = new DefaultNotificationManager(new NotificationChannel[] {emailChannel, twitterChannel}, getSessionFactory(), propertiesDao);
+ manager = new DefaultNotificationManager(new NotificationChannel[] {emailChannel, twitterChannel}, notificationQueueDao, propertiesDao);
}
@Test
public void shouldProvideChannelList() {
assertThat(manager.getChannels()).containsOnly(emailChannel, twitterChannel);
- manager = new DefaultNotificationManager(getSessionFactory(), propertiesDao);
+ manager = new DefaultNotificationManager(notificationQueueDao, propertiesDao);
assertThat(manager.getChannels()).hasSize(0);
}
@@ -79,10 +88,21 @@ public class DefaultNotificationManagerTest extends AbstractDbUnitTestCase {
Notification notification = new Notification("test");
manager.scheduleForSending(notification);
- NotificationQueueElement queueElement = manager.getFromQueue();
- assertThat(queueElement.getNotification(), is(notification));
+ verify(notificationQueueDao, only()).insert(any(NotificationQueueDto.class));
+ }
+
+ @Test
+ public void shouldGetFromQueueAndDelete() throws Exception {
+ Notification notification = new Notification("test");
+ NotificationQueueDto dto = NotificationQueueDto.toNotificationQueueDto(notification);
+ List<NotificationQueueDto> dtos = Arrays.asList(dto);
+ when(notificationQueueDao.findOldest(1)).thenReturn(dtos);
+
+ assertThat(manager.getFromQueue()).isNotNull();
- assertThat(manager.getFromQueue(), nullValue());
+ InOrder inOrder = inOrder(notificationQueueDao);
+ inOrder.verify(notificationQueueDao).findOldest(1);
+ inOrder.verify(notificationQueueDao).delete(dtos);
}
@Test
@@ -116,7 +136,7 @@ public class DefaultNotificationManagerTest extends AbstractDbUnitTestCase {
when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", null)).thenReturn(Lists.newArrayList("user3"));
when(propertiesDao.findUsersForNotification("NewAlerts", "Twitter", null)).thenReturn(Lists.newArrayList("user4"));
- Multimap<String, NotificationChannel> multiMap = manager.findSubscribedRecipientsForDispatcher(dispatcher, (Integer)null);
+ Multimap<String, NotificationChannel> multiMap = manager.findSubscribedRecipientsForDispatcher(dispatcher, (Integer) null);
assertThat(multiMap.entries()).hasSize(3);
Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
diff --git a/sonar-core/src/test/java/org/sonar/core/notification/db/NotificationQueueDaoTest.java b/sonar-core/src/test/java/org/sonar/core/notification/db/NotificationQueueDaoTest.java
new file mode 100644
index 00000000000..ec1f37e82aa
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/notification/db/NotificationQueueDaoTest.java
@@ -0,0 +1,75 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.core.notification.db;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.notifications.Notification;
+import org.sonar.core.persistence.AbstractDaoTestCase;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class NotificationQueueDaoTest extends AbstractDaoTestCase {
+
+ NotificationQueueDao dao;
+
+ @Before
+ public void createDao() {
+ dao = new NotificationQueueDao(getMyBatis());
+ }
+
+ @Test
+ public void should_insert_new_notification_queue() {
+ NotificationQueueDto notificationQueueDto = NotificationQueueDto.toNotificationQueueDto(new Notification("email"));
+
+ dao.insert(notificationQueueDto);
+
+ checkTables("should_insert_new_notification_queue", new String[] {"id", "created_at"}, "notifications");
+ assertThat(dao.findOldest(1).get(0).toNotification().getType()).isEqualTo("email");
+ }
+
+ @Test
+ public void should_delete_notification() {
+ setupData("should_delete_notification");
+
+ NotificationQueueDto dto1 = new NotificationQueueDto().setId(1L);
+ NotificationQueueDto dto3 = new NotificationQueueDto().setId(3L);
+
+ dao.delete(Arrays.asList(dto1, dto3));
+
+ checkTables("should_delete_notification", "notifications");
+ }
+
+ @Test
+ public void should_findOldest() {
+ setupData("should_findOldest");
+
+ Collection<NotificationQueueDto> result = dao.findOldest(3);
+ assertThat(result).hasSize(3);
+ assertThat(result).onProperty("id").containsOnly(1L, 2L, 4L);
+
+ result = dao.findOldest(6);
+ assertThat(result).hasSize(4);
+ }
+}
diff --git a/sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_delete_notification-result.xml b/sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_delete_notification-result.xml
new file mode 100644
index 00000000000..f289e5d1fb2
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_delete_notification-result.xml
@@ -0,0 +1,13 @@
+<dataset>
+
+ <notifications id="2" created_at="2013-08-27" data="rO0ABXNyAChvcmcuc29uYXIuYXBpLm5vdGlmaWNhdGlvbnMuTm90aWZpY2F0aW9uTppHnJFK4aAC
+AAJMAAZmaWVsZHN0AA9MamF2YS91dGlsL01hcDtMAAR0eXBldAASTGphdmEvbGFuZy9TdHJpbmc7
+eHBzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hv
+bGR4cD9AAAAAAAAMdwgAAAAQAAAAAHh0AAZlbWFpbDI=" />
+
+ <notifications id="4" created_at="2013-08-28" data="rO0ABXNyAChvcmcuc29uYXIuYXBpLm5vdGlmaWNhdGlvbnMuTm90aWZpY2F0aW9uTppHnJFK4aAC
+AAJMAAZmaWVsZHN0AA9MamF2YS91dGlsL01hcDtMAAR0eXBldAASTGphdmEvbGFuZy9TdHJpbmc7
+eHBzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hv
+bGR4cD9AAAAAAAAMdwgAAAAQAAAAAHh0AAZlbWFpbDQ=" />
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_delete_notification.xml b/sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_delete_notification.xml
new file mode 100644
index 00000000000..feeca98cc74
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_delete_notification.xml
@@ -0,0 +1,23 @@
+<dataset>
+
+ <notifications id="1" created_at="2013-08-26" data="rO0ABXNyAChvcmcuc29uYXIuYXBpLm5vdGlmaWNhdGlvbnMuTm90aWZpY2F0aW9uTppHnJFK4aAC
+AAJMAAZmaWVsZHN0AA9MamF2YS91dGlsL01hcDtMAAR0eXBldAASTGphdmEvbGFuZy9TdHJpbmc7
+eHBzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hv
+bGR4cD9AAAAAAAAMdwgAAAAQAAAAAHh0AAZlbWFpbDE=" />
+
+ <notifications id="2" created_at="2013-08-27" data="rO0ABXNyAChvcmcuc29uYXIuYXBpLm5vdGlmaWNhdGlvbnMuTm90aWZpY2F0aW9uTppHnJFK4aAC
+AAJMAAZmaWVsZHN0AA9MamF2YS91dGlsL01hcDtMAAR0eXBldAASTGphdmEvbGFuZy9TdHJpbmc7
+eHBzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hv
+bGR4cD9AAAAAAAAMdwgAAAAQAAAAAHh0AAZlbWFpbDI=" />
+
+ <notifications id="3" created_at="2013-08-29" data="rO0ABXNyAChvcmcuc29uYXIuYXBpLm5vdGlmaWNhdGlvbnMuTm90aWZpY2F0aW9uTppHnJFK4aAC
+AAJMAAZmaWVsZHN0AA9MamF2YS91dGlsL01hcDtMAAR0eXBldAASTGphdmEvbGFuZy9TdHJpbmc7
+eHBzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hv
+bGR4cD9AAAAAAAAMdwgAAAAQAAAAAHh0AAZlbWFpbDM=" />
+
+ <notifications id="4" created_at="2013-08-28" data="rO0ABXNyAChvcmcuc29uYXIuYXBpLm5vdGlmaWNhdGlvbnMuTm90aWZpY2F0aW9uTppHnJFK4aAC
+AAJMAAZmaWVsZHN0AA9MamF2YS91dGlsL01hcDtMAAR0eXBldAASTGphdmEvbGFuZy9TdHJpbmc7
+eHBzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hv
+bGR4cD9AAAAAAAAMdwgAAAAQAAAAAHh0AAZlbWFpbDQ=" />
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_findOldest.xml b/sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_findOldest.xml
new file mode 100644
index 00000000000..feeca98cc74
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_findOldest.xml
@@ -0,0 +1,23 @@
+<dataset>
+
+ <notifications id="1" created_at="2013-08-26" data="rO0ABXNyAChvcmcuc29uYXIuYXBpLm5vdGlmaWNhdGlvbnMuTm90aWZpY2F0aW9uTppHnJFK4aAC
+AAJMAAZmaWVsZHN0AA9MamF2YS91dGlsL01hcDtMAAR0eXBldAASTGphdmEvbGFuZy9TdHJpbmc7
+eHBzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hv
+bGR4cD9AAAAAAAAMdwgAAAAQAAAAAHh0AAZlbWFpbDE=" />
+
+ <notifications id="2" created_at="2013-08-27" data="rO0ABXNyAChvcmcuc29uYXIuYXBpLm5vdGlmaWNhdGlvbnMuTm90aWZpY2F0aW9uTppHnJFK4aAC
+AAJMAAZmaWVsZHN0AA9MamF2YS91dGlsL01hcDtMAAR0eXBldAASTGphdmEvbGFuZy9TdHJpbmc7
+eHBzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hv
+bGR4cD9AAAAAAAAMdwgAAAAQAAAAAHh0AAZlbWFpbDI=" />
+
+ <notifications id="3" created_at="2013-08-29" data="rO0ABXNyAChvcmcuc29uYXIuYXBpLm5vdGlmaWNhdGlvbnMuTm90aWZpY2F0aW9uTppHnJFK4aAC
+AAJMAAZmaWVsZHN0AA9MamF2YS91dGlsL01hcDtMAAR0eXBldAASTGphdmEvbGFuZy9TdHJpbmc7
+eHBzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hv
+bGR4cD9AAAAAAAAMdwgAAAAQAAAAAHh0AAZlbWFpbDM=" />
+
+ <notifications id="4" created_at="2013-08-28" data="rO0ABXNyAChvcmcuc29uYXIuYXBpLm5vdGlmaWNhdGlvbnMuTm90aWZpY2F0aW9uTppHnJFK4aAC
+AAJMAAZmaWVsZHN0AA9MamF2YS91dGlsL01hcDtMAAR0eXBldAASTGphdmEvbGFuZy9TdHJpbmc7
+eHBzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hv
+bGR4cD9AAAAAAAAMdwgAAAAQAAAAAHh0AAZlbWFpbDQ=" />
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_insert_new_notification_queue-result.xml b/sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_insert_new_notification_queue-result.xml
new file mode 100644
index 00000000000..f6c81b15f64
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/notification/db/NotificationQueueDaoTest/should_insert_new_notification_queue-result.xml
@@ -0,0 +1,8 @@
+<dataset>
+
+ <notifications id="1" created_at="[null]" data="rO0ABXNyAChvcmcuc29uYXIuYXBpLm5vdGlmaWNhdGlvbnMuTm90aWZpY2F0aW9uTppHnJFK4aAC
+AAJMAAZmaWVsZHN0AA9MamF2YS91dGlsL01hcDtMAAR0eXBldAASTGphdmEvbGFuZy9TdHJpbmc7
+eHBzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hv
+bGR4cD9AAAAAAAAMdwgAAAAQAAAAAHh0AAVlbWFpbA==" />
+
+</dataset>
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java
index 29d1690e1e7..c2b6dbf4ab8 100644
--- a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java
+++ b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java
@@ -33,7 +33,6 @@ import org.sonar.api.notifications.NotificationChannel;
import org.sonar.api.notifications.NotificationDispatcher;
import org.sonar.api.utils.TimeProfiler;
import org.sonar.core.notification.DefaultNotificationManager;
-import org.sonar.core.notification.NotificationQueueElement;
import java.util.Arrays;
import java.util.Collection;
@@ -115,13 +114,13 @@ public class NotificationService implements ServerComponent {
synchronized void processQueue() {
TIME_PROFILER.start("Processing notifications queue");
- NotificationQueueElement queueElement = manager.getFromQueue();
- while (queueElement != null) {
- deliver(queueElement.getNotification());
+ Notification notifToSend = manager.getFromQueue();
+ while (notifToSend != null) {
+ deliver(notifToSend);
if (stopping) {
break;
}
- queueElement = manager.getFromQueue();
+ notifToSend = manager.getFromQueue();
}
TIME_PROFILER.stop();
diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java
index 1e3c109a206..7c14b46a8f5 100644
--- a/sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java
@@ -27,7 +27,6 @@ import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
import org.sonar.api.notifications.NotificationDispatcher;
import org.sonar.core.notification.DefaultNotificationManager;
-import org.sonar.core.notification.NotificationQueueElement;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.any;
@@ -46,7 +45,6 @@ public class NotificationServiceTest {
private static String ASSIGNEE_SIMON = "simon";
private final DefaultNotificationManager manager = mock(DefaultNotificationManager.class);
- private final NotificationQueueElement queueElement = mock(NotificationQueueElement.class);
private final Notification notification = mock(Notification.class);
private final NotificationChannel emailChannel = mock(NotificationChannel.class);
private final NotificationChannel gtalkChannel = mock(NotificationChannel.class);
@@ -60,13 +58,12 @@ public class NotificationServiceTest {
when(gtalkChannel.getKey()).thenReturn("gtalk");
when(commentOnReviewAssignedToMe.getKey()).thenReturn("comment on review assigned to me");
when(commentOnReviewCreatedByMe.getKey()).thenReturn("comment on review created by me");
- when(queueElement.getNotification()).thenReturn(notification);
- when(manager.getFromQueue()).thenReturn(queueElement).thenReturn(null);
+ when(manager.getFromQueue()).thenReturn(notification).thenReturn(null);
Settings settings = new Settings().setProperty("sonar.notifications.delay", 1L);
service = new NotificationService(settings, manager,
- new NotificationDispatcher[] {commentOnReviewAssignedToMe, commentOnReviewCreatedByMe});
+ new NotificationDispatcher[] {commentOnReviewAssignedToMe, commentOnReviewCreatedByMe});
}
/**
@@ -132,7 +129,7 @@ public class NotificationServiceTest {
public void scenario3() {
setUpMocks(CREATOR_EVGENY, ASSIGNEE_SIMON);
doAnswer(addUser(ASSIGNEE_SIMON, new NotificationChannel[] {emailChannel, gtalkChannel}))
- .when(commentOnReviewAssignedToMe).dispatch(same(notification), any(NotificationDispatcher.Context.class));
+ .when(commentOnReviewAssignedToMe).dispatch(same(notification), any(NotificationDispatcher.Context.class));
service.start();
verify(emailChannel, timeout(2000)).deliver(notification, ASSIGNEE_SIMON);
@@ -168,8 +165,7 @@ public class NotificationServiceTest {
@Test
public void shouldNotStopWhenException() {
setUpMocks(CREATOR_SIMON, ASSIGNEE_SIMON);
- when(queueElement.getNotification()).thenThrow(new RuntimeException("Unexpected exception")).thenReturn(notification);
- when(manager.getFromQueue()).thenReturn(queueElement).thenReturn(queueElement).thenReturn(null);
+ when(manager.getFromQueue()).thenThrow(new RuntimeException("Unexpected exception")).thenReturn(notification).thenReturn(null);
doAnswer(addUser(ASSIGNEE_SIMON, emailChannel)).when(commentOnReviewAssignedToMe).dispatch(same(notification), any(NotificationDispatcher.Context.class));
doAnswer(addUser(CREATOR_SIMON, emailChannel)).when(commentOnReviewCreatedByMe).dispatch(same(notification), any(NotificationDispatcher.Context.class));