From 0036dc4829963449f22d966fbf2ad8ff4b4ae7cf Mon Sep 17 00:00:00 2001 From: kske Date: Fri, 19 Feb 2021 16:04:49 +0100 Subject: [PATCH] Add DeadEvent A dead events wraps an event that was dispatched but not delivered to any handler. The dead event is than dispatched to dedicated handlers. --- .../dev/kske/eventbus/core/DeadEvent.java | 32 ++++++++++++++++ .../java/dev/kske/eventbus/core/EventBus.java | 34 ++++++++++------- .../dev/kske/eventbus/core/EventHandler.java | 2 +- .../java/dev/kske/eventbus/core/DeadTest.java | 37 +++++++++++++++++++ 4 files changed, 90 insertions(+), 15 deletions(-) create mode 100644 event-bus-core/src/main/java/dev/kske/eventbus/core/DeadEvent.java create mode 100644 event-bus-core/src/test/java/dev/kske/eventbus/core/DeadTest.java diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/DeadEvent.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/DeadEvent.java new file mode 100644 index 0000000..0c93b9a --- /dev/null +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/DeadEvent.java @@ -0,0 +1,32 @@ +package dev.kske.eventbus.core; + +/** + * Wraps an event that was dispatched but for which no handler has been bound. + *

+ * Handling dead events is useful as it can identify a poorly configured event distribution. + * + * @author Kai S. K. Engelbart + * @since 1.1.0 + */ +public final class DeadEvent { + + private final EventBus eventBus; + private final Object event; + + DeadEvent(EventBus eventBus, Object event) { + this.eventBus = eventBus; + this.event = event; + } + + /** + * @return the event bus that originated this event + * @since 1.1.0 + */ + public EventBus getEventBus() { return eventBus; } + + /** + * @return the event that could not be delivered + * @since 1.1.0 + */ + public Object getEvent() { return event; } +} diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventBus.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventBus.java index 8f30e89..f0c065d 100644 --- a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventBus.java +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventBus.java @@ -73,14 +73,19 @@ public final class EventBus { var state = dispatchState.get(); state.isDispatching = true; - for (var handler : getHandlersFor(event.getClass())) - if (state.isCancelled) { - logger.log(Level.INFO, "Cancelled dispatching event {0}", event); - state.isCancelled = false; - break; - } else { - handler.execute(event); - } + Iterator handlers = getHandlersFor(event.getClass()); + if (handlers.hasNext()) { + while (handlers.hasNext()) + if (state.isCancelled) { + logger.log(Level.INFO, "Cancelled dispatching event {0}", event); + state.isCancelled = false; + break; + } else { + handlers.next().execute(event); + } + } else if (!(event instanceof DeadEvent)) { + dispatch(new DeadEvent(this, event)); + } // Reset dispatch state state.isDispatching = false; @@ -89,25 +94,26 @@ public final class EventBus { } /** - * Searches for the event handlers bound to an event class. + * Searches for the event handlers bound to an event class. This includes polymorphic handlers + * that are bound to a supertype of the event class. * * @param eventClass the event class to use for the search - * @return all event handlers registered for the event class + * @return an iterator over the applicable handlers in descending order of priority * @since 0.0.1 */ - private List getHandlersFor(Class eventClass) { + private Iterator getHandlersFor(Class eventClass) { // Get handlers defined for the event class - Set handlers = bindings.getOrDefault(eventClass, new TreeSet<>()); + TreeSet handlers = bindings.getOrDefault(eventClass, new TreeSet<>()); - // Get subtype handlers + // Get polymorphic handlers for (var binding : bindings.entrySet()) if (binding.getKey().isAssignableFrom(eventClass)) for (var handler : binding.getValue()) if (handler.isPolymorphic()) handlers.add(handler); - return new ArrayList<>(handlers); + return handlers.iterator(); } /** diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java index 91c2078..4c89174 100644 --- a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java @@ -68,7 +68,7 @@ final class EventHandler implements Comparable { * Compares this to another event handler based on priority. In case of equal priority a * non-zero value based on hash codes is returned. *

- * This is used to retrieve event handlers in order of descending priority from a tree set. + * This is used to retrieve event handlers in descending order of priority from a tree set. * * @since 0.0.1 */ diff --git a/event-bus-core/src/test/java/dev/kske/eventbus/core/DeadTest.java b/event-bus-core/src/test/java/dev/kske/eventbus/core/DeadTest.java new file mode 100644 index 0000000..94e925d --- /dev/null +++ b/event-bus-core/src/test/java/dev/kske/eventbus/core/DeadTest.java @@ -0,0 +1,37 @@ +package dev.kske.eventbus.core; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.*; + +/** + * Tests the dispatching of a dead event if an event could not be delivered. + * + * @author Kai S. K. Engelbart + * @since 1.1.0 + */ +class DeadTest { + + EventBus bus; + String event = "This event has no handler"; + boolean deadEventHandled; + + @BeforeEach + void registerListener() { + bus = new EventBus(); + bus.registerListener(this); + } + + @Test + void testDeadEvent() { + bus.dispatch(event); + assertTrue(deadEventHandled); + } + + @Event + void onDeadEvent(DeadEvent deadEvent) { + assertEquals(bus, deadEvent.getEventBus()); + assertEquals(event, deadEvent.getEvent()); + deadEventHandled = true; + } +}