From 6eebd3c121b0b8fb93cb12e5aec66756ab578451 Mon Sep 17 00:00:00 2001 From: kske Date: Wed, 7 Jul 2021 22:06:07 +0200 Subject: [PATCH 1/3] Pass errors caused during system event dispatch to caller When an error is caused during the dispatch of a system event, a warning has been logged instead instead of rethrowing the error. This has been fixed. This enables failing a JUnit test when an exception event handler is invoked. --- .../src/main/java/dev/kske/eventbus/core/EventBus.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 45c9ef8..2711e5d 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 @@ -94,14 +94,14 @@ public final class EventBus { try { handlers.next().execute(event); } catch (InvocationTargetException e) { - if (event instanceof DeadEvent || event instanceof ExceptionEvent) - - // Warn about system event not being handled - logger.log(Level.WARNING, event + " not handled due to exception", e); - else if (e.getCause() instanceof Error) + if (e.getCause() instanceof Error) // Transparently pass error to the caller throw (Error) e.getCause(); + else if (event instanceof DeadEvent || event instanceof ExceptionEvent) + + // Warn about system event not being handled + logger.log(Level.WARNING, event + " not handled due to exception", e); else // Dispatch exception event From 74447dea5919209ab89a6bf0706f9559302c041f Mon Sep 17 00:00:00 2001 From: kske Date: Wed, 7 Jul 2021 22:11:54 +0200 Subject: [PATCH 2/3] Add nested dispatch test The test performs a nested event dispatch then cancels the dispatch. If Both operations are successful, the test is successful. Currently, the test fails, but should be successful once the nested dispatch bug is fixed. --- .../dev/kske/eventbus/core/NestedTest.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 event-bus-core/src/test/java/dev/kske/eventbus/core/NestedTest.java diff --git a/event-bus-core/src/test/java/dev/kske/eventbus/core/NestedTest.java b/event-bus-core/src/test/java/dev/kske/eventbus/core/NestedTest.java new file mode 100644 index 0000000..bb0670e --- /dev/null +++ b/event-bus-core/src/test/java/dev/kske/eventbus/core/NestedTest.java @@ -0,0 +1,73 @@ +package dev.kske.eventbus.core; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.*; + +/** + * Tests nested event dispatches. + * + * @author Kai S. K. Engelbart + * @since 1.2.0 + */ +class NestedTest { + + EventBus bus; + boolean nestedHit; + + /** + * Constructs an event bus and registers this test instance as an event listener. + * + * @since 1.2.0 + */ + @BeforeEach + void registerListener() { + bus = new EventBus(); + bus.registerListener(this); + } + + /** + * Dispatches a simple event, which should in turn cause a string to be dispatched as a nested + * event. If the corresponding handler sets {@link #nestedHit} to {@code true}, the test is + * successful. + * + * @since 1.2.0 + */ + @Test + void testNestedDispatch() { + bus.dispatch(new SimpleEvent()); + assertTrue(nestedHit); + } + + /** + * Dispatches a string as a nested event and cancels the current dispatch afterwards. + * + * @since 1.2.0 + */ + @Event(SimpleEvent.class) + void onSimpleEvent() { + bus.dispatch("Nested event"); + bus.cancel(); + } + + /** + * Sets {@link #nestedHit} to {@code true} indicating that nested dispatches work. + * + * @since 1.2.0 + */ + @Event(String.class) + void onString() { + nestedHit = true; + } + + /** + * Fails the test if an exception is caused during the dispatch. + * + * @param e the event containing the exception + * @since 1.2.0 + */ + @Event + void onException(ExceptionEvent e) { + fail("Exception during dispatch", e.getCause()); + } +} From 205a183db71ea0f64469c1a79700d59bda1e7a10 Mon Sep 17 00:00:00 2001 From: kske Date: Mon, 12 Jul 2021 10:19:04 +0200 Subject: [PATCH 3/3] Allow nested dispatches by keeping track of nesting count --- .../java/dev/kske/eventbus/core/EventBus.java | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) 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 2711e5d..27bb4fb 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 @@ -27,7 +27,21 @@ public final class EventBus { */ private static final class DispatchState { - boolean isDispatching, isCancelled; + /** + * Indicates that the last event handler invoked has called {@link EventBus#cancel}. In that + * case, the event is not dispatched further. + * + * @since 0.1.0 + */ + boolean isCancelled; + + /** + * Is incremented when {@link EventBus#dispatch(Object)} is invoked and decremented when it + * finishes. This allows keeping track of nested dispatches. + * + * @since 1.2.0 + */ + int nestingCount; } /** @@ -79,9 +93,11 @@ public final class EventBus { Objects.requireNonNull(event); logger.log(Level.INFO, "Dispatching event {0}", event); - // Set dispatch state + // Look up dispatch state var state = dispatchState.get(); - state.isDispatching = true; + + // Increment nesting count (becomes > 1 during nested dispatches) + ++state.nestingCount; Iterator handlers = getHandlersFor(event.getClass()); if (handlers.hasNext()) { @@ -118,8 +134,8 @@ public final class EventBus { dispatch(new DeadEvent(this, event)); } - // Reset dispatch state - state.isDispatching = false; + // Decrement nesting count (becomes 0 when all dispatches on the thread are finished) + --state.nestingCount; logger.log(Level.DEBUG, "Finished dispatching event {0}", event); } @@ -155,7 +171,7 @@ public final class EventBus { */ public void cancel() { var state = dispatchState.get(); - if (state.isDispatching && !state.isCancelled) + if (state.nestingCount > 0 && !state.isCancelled) state.isCancelled = true; else throw new EventBusException("Calling thread not an active dispatching thread!");