Compare commits

...

4 Commits

Author SHA1 Message Date
b915a5c490
Merge pull request 'Properly Handle Nested Dispatches' (#19) from b/nested-dispatch into develop
Reviewed-on: https://git.kske.dev/kske/event-bus/pulls/19
Reviewed-by: delvh <leon@kske.dev>
2021-07-12 11:25:04 +02:00
205a183db7
Allow nested dispatches by keeping track of nesting count 2021-07-12 10:24:48 +02:00
74447dea59
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.
2021-07-12 10:17:46 +02:00
6eebd3c121
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.
2021-07-07 22:06:07 +02:00
2 changed files with 100 additions and 11 deletions

View File

@ -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<EventHandler> handlers = getHandlersFor(event.getClass());
if (handlers.hasNext()) {
@ -94,14 +110,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
@ -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!");

View File

@ -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());
}
}