NotificationService.java

1
package com.reallifedeveloper.common.application.notification;
2
3
import static com.reallifedeveloper.common.domain.LogUtil.removeCRLF;
4
5
import java.io.IOException;
6
import java.util.ArrayList;
7
import java.util.List;
8
9
import org.slf4j.Logger;
10
import org.slf4j.LoggerFactory;
11
import org.springframework.transaction.annotation.Transactional;
12
13
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
14
15
import com.reallifedeveloper.common.application.eventstore.EventStore;
16
import com.reallifedeveloper.common.application.eventstore.StoredEvent;
17
import com.reallifedeveloper.common.domain.ErrorHandling;
18
19
/**
20
 * An application service to work with {@link NotificationLog NotificationLogs}.
21
 *
22
 * @author RealLifeDeveloper
23
 */
24
public class NotificationService {
25
26
    private static final Logger LOG = LoggerFactory.getLogger(NotificationService.class);
27
28
    private final EventStore eventStore;
29
30
    private final PublishedMessageTrackerRepository messageTrackerRepository;
31
32
    private final NotificationPublisher notificationPublisher;
33
34
    /**
35
     * Creates a new {@code NotificationService} that uses the given components.
36
     *
37
     * @param eventStore               an event store for finding stored domain events
38
     * @param messageTrackerRepository a repository for keeping track of the last notification published
39
     * @param notificationPublisher    a publisher of notifications to external systems
40
     *
41
     * @throws IllegalArgumentException if any argument is {@code null}
42
     */
43
    @SuppressFBWarnings("EI_EXPOSE_REP2")
44
    public NotificationService(EventStore eventStore, PublishedMessageTrackerRepository messageTrackerRepository,
45
            NotificationPublisher notificationPublisher) {
46 1 1. <init> : removed call to com/reallifedeveloper/common/domain/ErrorHandling::checkNull → KILLED
        ErrorHandling.checkNull("Arguments must not be null: eventStore=%s, messageTrackerRepository=%s, notificationPublisher=%s",
47
                eventStore, messageTrackerRepository, notificationPublisher);
48
        this.eventStore = eventStore;
49
        this.messageTrackerRepository = messageTrackerRepository;
50
        this.notificationPublisher = notificationPublisher;
51
    }
52
53
    /**
54
     * Gives a {@link NotificationLog} containing the most recent {@link Notification Notifications}.
55
     *
56
     * @param batchSize the maximum number of notifications in the notification log
57
     *
58
     * @return a notification log with the most recent notifications
59
     */
60
    @Transactional(readOnly = true)
61
    public NotificationLog currentNotificationLog(int batchSize) {
62
        LOG.trace("currentNotificationLog: batchSize={}", batchSize);
63
        NotificationLogId notificationLogId = calculateCurrentNotificationLogId(batchSize);
64
        NotificationLog notificationLog = findNotificationLog(notificationLogId);
65
        LOG.trace("currentNotificationLog: {}", notificationLog);
66 1 1. currentNotificationLog : replaced return value with null for com/reallifedeveloper/common/application/notification/NotificationService::currentNotificationLog → KILLED
        return notificationLog;
67
    }
68
69
    /**
70
     * Gives an archived {@link NotificationLog}.
71
     *
72
     * @param notificationLogId represents the first and last {@link Notification} in the log
73
     *
74
     * @return an archived {@code NotificationLog}
75
     */
76
    @Transactional(readOnly = true)
77
    @SuppressFBWarnings(value = "CRLF_INJECTION_LOGS", justification = "Logging only of objects, not user data")
78
    public NotificationLog notificationLog(NotificationLogId notificationLogId) {
79
        LOG.trace("notificationLog: notificationLogId={}", notificationLogId);
80 1 1. notificationLog : removed call to com/reallifedeveloper/common/domain/ErrorHandling::checkNull → SURVIVED
        ErrorHandling.checkNull("notificationLogId must not be null", notificationLogId);
81
        NotificationLog notificationLog = findNotificationLog(notificationLogId);
82
        LOG.trace("notificationLog: {}", notificationLog);
83 1 1. notificationLog : replaced return value with null for com/reallifedeveloper/common/application/notification/NotificationService::notificationLog → KILLED
        return notificationLog;
84
    }
85
86
    private NotificationLog findNotificationLog(NotificationLogId notificationLogId) {
87
        List<StoredEvent> storedEvents = eventStore.allEventsBetween(notificationLogId.low(), notificationLogId.high());
88
        long lastStoredEventId = eventStore.lastStoredEventId();
89 2 1. findNotificationLog : negated conditional → KILLED
2. findNotificationLog : changed conditional boundary → KILLED
        boolean archivedIndicator = notificationLogId.high() <= lastStoredEventId;
90 2 1. findNotificationLog : changed conditional boundary → KILLED
2. findNotificationLog : negated conditional → KILLED
        NotificationLogId next = notificationLogId.high() < lastStoredEventId ? notificationLogId.next() : null;
91 2 1. findNotificationLog : negated conditional → KILLED
2. findNotificationLog : changed conditional boundary → KILLED
        NotificationLogId previous = notificationLogId.low() > 1 ? notificationLogId.previous() : null;
92 1 1. findNotificationLog : replaced return value with null for com/reallifedeveloper/common/application/notification/NotificationService::findNotificationLog → KILLED
        return new NotificationLog(notificationLogId, next, previous, notificationsFrom(storedEvents), archivedIndicator);
93
    }
94
95
    private NotificationLogId calculateCurrentNotificationLogId(int batchSize) {
96
        long count = eventStore.lastStoredEventId();
97 1 1. calculateCurrentNotificationLogId : Replaced long modulus with multiplication → KILLED
        long remainder = count % batchSize;
98 1 1. calculateCurrentNotificationLogId : negated conditional → KILLED
        if (remainder == 0) {
99
            remainder = batchSize;
100
        }
101 2 1. calculateCurrentNotificationLogId : Replaced long subtraction with addition → KILLED
2. calculateCurrentNotificationLogId : Replaced long addition with subtraction → KILLED
        long low = count - remainder + 1;
102 2 1. calculateCurrentNotificationLogId : changed conditional boundary → SURVIVED
2. calculateCurrentNotificationLogId : negated conditional → KILLED
        if (low < 1) {
103
            low = 1;
104
        }
105 2 1. calculateCurrentNotificationLogId : Replaced long addition with subtraction → KILLED
2. calculateCurrentNotificationLogId : Replaced long subtraction with addition → KILLED
        long high = low + batchSize - 1;
106 1 1. calculateCurrentNotificationLogId : replaced return value with null for com/reallifedeveloper/common/application/notification/NotificationService::calculateCurrentNotificationLogId → KILLED
        return new NotificationLogId(low, high);
107
    }
108
109
    private List<Notification> notificationsFrom(List<StoredEvent> storedEvents) {
110
        List<Notification> notifications = new ArrayList<>();
111
        NotificationFactory notificationFactory = NotificationFactory.instance(eventStore);
112
        for (StoredEvent storedEvent : storedEvents) {
113
            notifications.add(notificationFactory.fromStoredEvent(storedEvent));
114
        }
115 1 1. notificationsFrom : replaced return value with Collections.emptyList for com/reallifedeveloper/common/application/notification/NotificationService::notificationsFrom → KILLED
        return notifications;
116
    }
117
118
    /**
119
     * Publishes notifications about all events that have occurred since the last publication to the given publication channel.
120
     *
121
     * @param publicationChannel the name of the publication channel to publish notifications on
122
     *
123
     * @throws IOException if publishing failed
124
     */
125
    @Transactional
126
    public void publishNotifications(String publicationChannel) throws IOException {
127
        LOG.trace("publishNotifications: publicationChannel={}", removeCRLF(publicationChannel));
128
        PublishedMessageTracker messageTracker = messageTracker(publicationChannel);
129
        List<Notification> notifications = unpublishedNotifications(messageTracker.lastPublishedMessageId());
130 1 1. publishNotifications : removed call to com/reallifedeveloper/common/application/notification/NotificationPublisher::publish → KILLED
        notificationPublisher.publish(notifications, publicationChannel);
131 1 1. publishNotifications : removed call to com/reallifedeveloper/common/application/notification/NotificationService::trackLastPublishedMessage → KILLED
        trackLastPublishedMessage(messageTracker, notifications);
132
        LOG.trace("publishNotifications: done");
133
    }
134
135
    private PublishedMessageTracker messageTracker(String publicationChannel) {
136 1 1. messageTracker : replaced return value with null for com/reallifedeveloper/common/application/notification/NotificationService::messageTracker → KILLED
        return messageTrackerRepository.findByPublicationChannel(publicationChannel)
137 1 1. lambda$messageTracker$0 : replaced return value with null for com/reallifedeveloper/common/application/notification/NotificationService::lambda$messageTracker$0 → KILLED
                .orElseGet(() -> new PublishedMessageTracker(0, publicationChannel));
138
    }
139
140
    private List<Notification> unpublishedNotifications(long lastPublishedMessageId) {
141
        List<StoredEvent> storedEvents = eventStore.allEventsSince(lastPublishedMessageId);
142 1 1. unpublishedNotifications : replaced return value with Collections.emptyList for com/reallifedeveloper/common/application/notification/NotificationService::unpublishedNotifications → KILLED
        return notificationsFrom(storedEvents);
143
    }
144
145
    private void trackLastPublishedMessage(PublishedMessageTracker messageTracker, List<Notification> notifications) {
146 1 1. trackLastPublishedMessage : negated conditional → KILLED
        if (!notifications.isEmpty()) {
147 1 1. trackLastPublishedMessage : Replaced integer subtraction with addition → KILLED
            Notification lastNotification = notifications.get(notifications.size() - 1);
148 1 1. trackLastPublishedMessage : removed call to com/reallifedeveloper/common/application/notification/PublishedMessageTracker::setLastPublishedMessageid → KILLED
            messageTracker.setLastPublishedMessageid(lastNotification.storedEventId());
149
            messageTrackerRepository.save(messageTracker);
150
        }
151
    }
152
153
    /**
154
     * Make finalize method final to avoid "Finalizer attacks" and corresponding SpotBugs warning (CT_CONSTRUCTOR_THROW).
155
     *
156
     * @see <a href="https://wiki.sei.cmu.edu/confluence/display/java/OBJ11-J.+Be+wary+of+letting+constructors+throw+exceptions">
157
     *      Explanation of finalizer attack</a>
158
     */
159
    @Override
160
    @Deprecated
161
    @SuppressWarnings({ "checkstyle:NoFinalizer", "PMD.EmptyFinalizer" })
162
    protected final void finalize() throws Throwable {
163
        // Do nothing
164
    }
165
166
}

Mutations

46

1.1
Location : <init>
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:constructorNullMessageTrackerRepository()]
removed call to com/reallifedeveloper/common/domain/ErrorHandling::checkNull → KILLED

66

1.1
Location : currentNotificationLog
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLogNoNotifications()]
replaced return value with null for com/reallifedeveloper/common/application/notification/NotificationService::currentNotificationLog → KILLED

80

1.1
Location : notificationLog
Killed by : none
removed call to com/reallifedeveloper/common/domain/ErrorHandling::checkNull → SURVIVED
Covering tests

83

1.1
Location : notificationLog
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:notificationLogNoNotifications()]
replaced return value with null for com/reallifedeveloper/common/application/notification/NotificationService::notificationLog → KILLED

89

1.1
Location : findNotificationLog
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLogNoNotifications()]
negated conditional → KILLED

2.2
Location : findNotificationLog
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLogFullBatch()]
changed conditional boundary → KILLED

90

1.1
Location : findNotificationLog
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLogFullBatch()]
changed conditional boundary → KILLED

2.2
Location : findNotificationLog
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLogNoNotifications()]
negated conditional → KILLED

91

1.1
Location : findNotificationLog
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLogNoNotifications()]
negated conditional → KILLED

2.2
Location : findNotificationLog
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLogNoNotifications()]
changed conditional boundary → KILLED

92

1.1
Location : findNotificationLog
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLogNoNotifications()]
replaced return value with null for com/reallifedeveloper/common/application/notification/NotificationService::findNotificationLog → KILLED

97

1.1
Location : calculateCurrentNotificationLogId
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLog()]
Replaced long modulus with multiplication → KILLED

98

1.1
Location : calculateCurrentNotificationLogId
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLog()]
negated conditional → KILLED

101

1.1
Location : calculateCurrentNotificationLogId
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLogNoNotifications()]
Replaced long subtraction with addition → KILLED

2.2
Location : calculateCurrentNotificationLogId
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLog()]
Replaced long addition with subtraction → KILLED

102

1.1
Location : calculateCurrentNotificationLogId
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

2.2
Location : calculateCurrentNotificationLogId
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLogNoNotifications()]
negated conditional → KILLED

105

1.1
Location : calculateCurrentNotificationLogId
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLogNoNotifications()]
Replaced long addition with subtraction → KILLED

2.2
Location : calculateCurrentNotificationLogId
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLogNoNotifications()]
Replaced long subtraction with addition → KILLED

106

1.1
Location : calculateCurrentNotificationLogId
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:currentNotificationLogNoNotifications()]
replaced return value with null for com/reallifedeveloper/common/application/notification/NotificationService::calculateCurrentNotificationLogId → KILLED

115

1.1
Location : notificationsFrom
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:publishSameNotificationsTwice()]
replaced return value with Collections.emptyList for com/reallifedeveloper/common/application/notification/NotificationService::notificationsFrom → KILLED

130

1.1
Location : publishNotifications
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:publishSameNotificationsTwice()]
removed call to com/reallifedeveloper/common/application/notification/NotificationPublisher::publish → KILLED

131

1.1
Location : publishNotifications
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:publishSameNotificationsTwice()]
removed call to com/reallifedeveloper/common/application/notification/NotificationService::trackLastPublishedMessage → KILLED

136

1.1
Location : messageTracker
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:publishSameNotificationsTwice()]
replaced return value with null for com/reallifedeveloper/common/application/notification/NotificationService::messageTracker → KILLED

137

1.1
Location : lambda$messageTracker$0
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:publishSameNotificationsTwice()]
replaced return value with null for com/reallifedeveloper/common/application/notification/NotificationService::lambda$messageTracker$0 → KILLED

142

1.1
Location : unpublishedNotifications
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:publishSameNotificationsTwice()]
replaced return value with Collections.emptyList for com/reallifedeveloper/common/application/notification/NotificationService::unpublishedNotifications → KILLED

146

1.1
Location : trackLastPublishedMessage
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:publishSameNotificationsTwice()]
negated conditional → KILLED

147

1.1
Location : trackLastPublishedMessage
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:publishSameNotificationsTwice()]
Replaced integer subtraction with addition → KILLED

148

1.1
Location : trackLastPublishedMessage
Killed by : com.reallifedeveloper.common.application.notification.NotificationServiceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.application.notification.NotificationServiceTest]/[method:publishSameNotificationsTwice()]
removed call to com/reallifedeveloper/common/application/notification/PublishedMessageTracker::setLastPublishedMessageid → KILLED

Active mutators

Tests examined


Report generated by PIT 1.20.0