ThreadLocalDomainEventPublisher.java

1
package com.reallifedeveloper.common.domain.event;
2
3
import java.util.ArrayList;
4
import java.util.List;
5
6
/**
7
 * A publisher of domain events that keeps track of subscribers on a per-thread basis. It is assumed that subscription and publishing are
8
 * done by the same thread, and publishing is handled synchronously. To handle events asynchronously, a subscriber could send a message to a
9
 * message queue, or store the event for later processing.
10
 * <p>
11
 * If threads are reused, it is important to call the {@link #reset()} method to clear any previous subscribers.
12
 * <p>
13
 * The normal use-case for this class is as follows:
14
 * <ul>
15
 * <li>A request comes in to an application service.</li>
16
 * <li>The application service creates or retrieves an instance of this class and calls the {@link #reset()} method.</li>
17
 * <li>The application service registers all necessary subscribers using the {@link #subscribe(DomainEventSubscriber)} method.</li>
18
 * <li>The application service delegates to domain services or aggregates, which publish events when something interesting happens in the
19
 * domain, using the {@link #publish(DomainEvent)} method.</li>
20
 * </ul>
21
 *
22
 * @author RealLifeDeveloper
23
 */
24
public class ThreadLocalDomainEventPublisher implements DomainEventPublisher {
25
26
    private static ThreadLocal<List<DomainEventSubscriber<DomainEvent>>> subscribers = ThreadLocal.withInitial(ArrayList::new);
27
28
    private static ThreadLocal<Boolean> publishing = ThreadLocal.withInitial(() -> false);
29
30
    /**
31
     * Registers an event handler with this publisher.
32
     *
33
     * @param subscriber the event handler to register
34
     *
35
     * @throws IllegalStateException if called while publishing events
36
     */
37
    @Override
38
    public void subscribe(DomainEventSubscriber<? extends DomainEvent> subscriber) {
39 1 1. subscribe : removed call to com/reallifedeveloper/common/domain/event/ThreadLocalDomainEventPublisher::checkPublishing → KILLED
        checkPublishing();
40
        @SuppressWarnings("unchecked")
41
        DomainEventSubscriber<DomainEvent> s = (DomainEventSubscriber<DomainEvent>) subscriber;
42
        subscribers().add(s);
43
    }
44
45
    /**
46
     * Publishes a domain event, i.e., calls the {@link DomainEventSubscriber#handleEvent(DomainEvent)} method for each registered
47
     * subscriber.
48
     *
49
     * @param event the domain event to publish
50
     *
51
     * @throws IllegalStateException if called while already publishing events
52
     */
53
    @Override
54
    public void publish(DomainEvent event) {
55 1 1. publish : removed call to com/reallifedeveloper/common/domain/event/ThreadLocalDomainEventPublisher::checkPublishing → KILLED
        checkPublishing();
56
        try {
57 1 1. publish : removed call to java/lang/ThreadLocal::set → KILLED
            publishing.set(true);
58
            for (DomainEventSubscriber<DomainEvent> subscriber : subscribers()) {
59 1 1. publish : negated conditional → KILLED
                if (subscriber.eventType().isAssignableFrom(event.getClass())) {
60 1 1. publish : removed call to com/reallifedeveloper/common/domain/event/DomainEventSubscriber::handleEvent → KILLED
                    subscriber.handleEvent(event);
61
                }
62
            }
63
        } finally {
64 1 1. publish : removed call to java/lang/ThreadLocal::set → KILLED
            publishing.set(false);
65
        }
66
    }
67
68
    /**
69
     * Removes all subscribers. Since subscribers are stored on a per-thread basis, and since threads may be reused, this method should be
70
     * called when starting to handle a new request.
71
     *
72
     * @throws IllegalStateException if called while publishing events
73
     */
74
    public void reset() {
75 1 1. reset : removed call to com/reallifedeveloper/common/domain/event/ThreadLocalDomainEventPublisher::checkPublishing → KILLED
        checkPublishing();
76 1 1. reset : removed call to java/util/List::clear → KILLED
        subscribers().clear();
77
    }
78
79
    private List<DomainEventSubscriber<DomainEvent>> subscribers() {
80 1 1. subscribers : replaced return value with Collections.emptyList for com/reallifedeveloper/common/domain/event/ThreadLocalDomainEventPublisher::subscribers → KILLED
        return subscribers.get();
81
    }
82
83
    private void checkPublishing() {
84 1 1. checkPublishing : negated conditional → KILLED
        if (publishing.get()) {
85
            throw new IllegalStateException("Method should not be called while publishing events");
86
        }
87
    }
88
}

Mutations

39

1.1
Location : subscribe
Killed by : com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest]/[method:subscribingSubscriberNotAllowed()]
removed call to com/reallifedeveloper/common/domain/event/ThreadLocalDomainEventPublisher::checkPublishing → KILLED

55

1.1
Location : publish
Killed by : com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest]/[method:publishingSubscriberNotAllowed()]
removed call to com/reallifedeveloper/common/domain/event/ThreadLocalDomainEventPublisher::checkPublishing → KILLED

57

1.1
Location : publish
Killed by : com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest]/[method:publishingSubscriberNotAllowed()]
removed call to java/lang/ThreadLocal::set → KILLED

59

1.1
Location : publish
Killed by : com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest]/[method:publishingSubscriberNotAllowed()]
negated conditional → KILLED

60

1.1
Location : publish
Killed by : com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest]/[method:publishingSubscriberNotAllowed()]
removed call to com/reallifedeveloper/common/domain/event/DomainEventSubscriber::handleEvent → KILLED

64

1.1
Location : publish
Killed by : com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest]/[method:canSubscribeToAllDomainEvents()]
removed call to java/lang/ThreadLocal::set → KILLED

75

1.1
Location : reset
Killed by : com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest]/[method:resettingSubscriberNotAllowed()]
removed call to com/reallifedeveloper/common/domain/event/ThreadLocalDomainEventPublisher::checkPublishing → KILLED

76

1.1
Location : reset
Killed by : com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest]/[method:reset()]
removed call to java/util/List::clear → KILLED

80

1.1
Location : subscribers
Killed by : com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest]/[method:publishingSubscriberNotAllowed()]
replaced return value with Collections.emptyList for com/reallifedeveloper/common/domain/event/ThreadLocalDomainEventPublisher::subscribers → KILLED

84

1.1
Location : checkPublishing
Killed by : com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.domain.event.ThreadLocalDomainEventPublisherTest]/[method:noSubscribers()]
negated conditional → KILLED

Active mutators

Tests examined


Report generated by PIT 1.20.0