MCPcopy Create free account
hub / github.com/InterviewReady/Low-Level-Design / EventBus

Class EventBus

distributed-event-bus/src/main/java/EventBus.java:19–202  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

17import java.util.function.Predicate;
18
19@Singleton
20public class EventBus {
21 private final Map<String, List<Event>> topics;
22 private final Map<String, Map<String, Integer>> eventIndexes;
23 private final Map<String, ConcurrentSkipListMap<Long, String>> eventTimestamps;
24 private final Map<String, Map<String, Subscription>> pullSubscriptions;
25 private final Map<String, Map<String, Subscription>> pushSubscriptions;
26 private final KeyedExecutor<String> eventExecutor;
27 private final KeyedExecutor<String> broadcastExecutor;
28 private final Timer timer;
29 private EventBus deadLetterQueue;
30
31 @Inject
32 public EventBus(final KeyedExecutor<String> eventExecutor, final KeyedExecutor<String> broadcastExecutor, final Timer timer) {
33 this.topics = new ConcurrentHashMap<>();
34 this.eventIndexes = new ConcurrentHashMap<>();
35 this.eventTimestamps = new ConcurrentHashMap<>();
36 this.pullSubscriptions = new ConcurrentHashMap<>();
37 this.pushSubscriptions = new ConcurrentHashMap<>();
38 this.eventExecutor = eventExecutor;
39 this.broadcastExecutor = broadcastExecutor;
40 this.timer = timer;
41 }
42
43 public void setDeadLetterQueue(final EventBus deadLetterQueue) {
44 this.deadLetterQueue = deadLetterQueue;
45 }
46
47 public CompletionStage<Void> publish(final String topic, final Event event) {
48 return eventExecutor.getThreadFor(topic, publishToBus(topic, event));
49 }
50
51 private CompletionStage<Void> publishToBus(final String topic, final Event event) {
52 if (eventIndexes.containsKey(topic) && eventIndexes.get(topic).containsKey(event.getId())) {
53 return null;
54 }
55 topics.putIfAbsent(topic, new CopyOnWriteArrayList<>());
56 eventIndexes.putIfAbsent(topic, new ConcurrentHashMap<>());
57 eventIndexes.get(topic).put(event.getId(), topics.get(topic).size());
58 eventTimestamps.putIfAbsent(topic, new ConcurrentSkipListMap<>());
59 eventTimestamps.get(topic).put(timer.getCurrentTime(), event.getId());
60 topics.get(topic).add(event);
61 return notifyPushSubscribers(topic, event);
62 }
63
64 private CompletionStage<Void> notifyPushSubscribers(String topic, Event event) {
65 if (!pushSubscriptions.containsKey(topic)) {
66 return CompletableFuture.completedStage(null);
67 }
68 final var subscribersForTopic = pushSubscriptions.get(topic);
69 final var notifications = subscribersForTopic.values()
70 .stream()
71 .filter(subscription -> subscription.getPrecondition().test(event))
72 .map(subscription -> executeEventHandler(event, subscription))
73 .toArray(CompletableFuture[]::new);
74 return CompletableFuture.allOf(notifications);
75 }
76

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected