Compare commits
7 Commits
1.1.0
...
866a547114
Author | SHA1 | Date | |
---|---|---|---|
866a547114
![]() |
|||
33ebf0302b
|
|||
b915a5c490
![]() |
|||
205a183db7
|
|||
74447dea59
|
|||
6eebd3c121
|
|||
b758f4cef1
|
@ -45,9 +45,6 @@ public class SimpleEventListener {
|
||||
}
|
||||
```
|
||||
|
||||
In this case, an event bus is created and used locally.
|
||||
In a more sophisticated example the class would acquire an external event bus that is used by multiple classes.
|
||||
|
||||
Note that creating static event handlers like this
|
||||
|
||||
```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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -38,26 +52,18 @@ public final class EventBus {
|
||||
*/
|
||||
public static final int DEFAULT_PRIORITY = 100;
|
||||
|
||||
private static volatile EventBus singletonInstance;
|
||||
private static final EventBus singletonInstance = new EventBus();
|
||||
|
||||
private static final Logger logger = System.getLogger(EventBus.class.getName());
|
||||
|
||||
/**
|
||||
* Produces a singleton instance of the event bus. It is lazily initialized on the first call.
|
||||
* Returns the default event bus, which is a statically initialized singleton instance.
|
||||
*
|
||||
* @return a singleton instance of the event bus.
|
||||
* @return the default event bus
|
||||
* @since 0.0.2
|
||||
*/
|
||||
public static EventBus getInstance() {
|
||||
EventBus instance = singletonInstance;
|
||||
if (instance == null)
|
||||
synchronized (EventBus.class) {
|
||||
if ((instance = singletonInstance) == null) {
|
||||
logger.log(Level.DEBUG, "Initializing singleton event bus instance");
|
||||
instance = singletonInstance = new EventBus();
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
return singletonInstance;
|
||||
}
|
||||
|
||||
private final Map<Class<?>, TreeSet<EventHandler>> bindings =
|
||||
@ -79,9 +85,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 +102,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 +126,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 +163,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!");
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user