Compare commits
27 Commits
1.1.0
...
11860d1469
Author | SHA1 | Date | |
---|---|---|---|
11860d1469
![]() |
|||
f620f06208
|
|||
5a6d8bcf35
|
|||
39ffb5c82a
|
|||
5ddef71c26
![]() |
|||
85b2da391a
![]() |
|||
46a358da97
|
|||
6bf9e1097a
|
|||
3fccb809c8
![]() |
|||
d1c4bcc7eb
![]() |
|||
ad29a93ccb
![]() |
|||
e67b64678b
![]() |
|||
c614beb063
|
|||
d3abb0aca3
|
|||
ee688929fd
|
|||
897d794b86
![]() |
|||
40d48cb959
![]() |
|||
b760c58298
|
|||
872b395374
|
|||
82c66c45ec
|
|||
866a547114
![]() |
|||
33ebf0302b
|
|||
b915a5c490
![]() |
|||
205a183db7
|
|||
74447dea59
|
|||
6eebd3c121
|
|||
b758f4cef1
|
68
README.md
68
README.md
@ -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
|
Note that creating static event handlers like this
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@ -57,6 +54,33 @@ private static void onSimpleEvent(SimpleEvent event) { ... }
|
|||||||
|
|
||||||
is technically possible, however you would still have to create an instance of the event listener to register it at an event bus.
|
is technically possible, however you would still have to create an instance of the event listener to register it at an event bus.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Event Bus is available in Maven Central.
|
||||||
|
To include it inside your project, just add the following dependency to your `pom.xml`:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.kske</groupId>
|
||||||
|
<artifactId>event-bus-core</artifactId>
|
||||||
|
<version>1.1.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, require the Event Bus Core module in your `module-info.java`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
requires dev.kske.eventbus.core;
|
||||||
|
```
|
||||||
|
|
||||||
|
If you intend to use event handlers that are inaccessible to Event Bus by means of Java language access control, make sure to allow reflective access to your package for Event Bus:
|
||||||
|
|
||||||
|
```java
|
||||||
|
opens my.package to dev.kske.eventbus.core;
|
||||||
|
```
|
||||||
|
|
||||||
## Polymorphic Event Handlers
|
## Polymorphic Event Handlers
|
||||||
|
|
||||||
On certain occasions it's practical for an event handler to accept both events of the specified type, as well as subclasses of that event.
|
On certain occasions it's practical for an event handler to accept both events of the specified type, as well as subclasses of that event.
|
||||||
@ -97,6 +121,18 @@ private void onSimpleEvent() {
|
|||||||
|
|
||||||
Make sure that you **do not** both declare a parameter and specify the event type in the annotation, as this would be ambiguous.
|
Make sure that you **do not** both declare a parameter and specify the event type in the annotation, as this would be ambiguous.
|
||||||
|
|
||||||
|
## Callback listeners
|
||||||
|
|
||||||
|
While defining event handlers as annotated methods is rather simple and readable, sometimes a more flexible approach is required.
|
||||||
|
For this reason, there are callback event handlers that allow the registration of an "inline" event listener consisting of just one handler in the form of a consumer:
|
||||||
|
|
||||||
|
```java
|
||||||
|
EventBus.getInstance().registerListener(SimpleEvent.class, e -> System.out.println("Received " + e));
|
||||||
|
```
|
||||||
|
|
||||||
|
The event type has to be defined explicitly, with the priority and polymorphism parameters being optional.
|
||||||
|
If you intend to remove the listener later, remember to keep a reference to it, as you would have to clear the entire event bus if you didn't.
|
||||||
|
|
||||||
## Listener-Level Properties
|
## Listener-Level Properties
|
||||||
|
|
||||||
When defining a dedicated event listener that, for example, performs pre- or post-processing, all event handlers will probably have the same non-standard priority.
|
When defining a dedicated event listener that, for example, performs pre- or post-processing, all event handlers will probably have the same non-standard priority.
|
||||||
@ -162,32 +198,16 @@ The same applies when an exception event handler throws an exception.
|
|||||||
|
|
||||||
To avoid this, system events never cause system events and instead just issue a warning to the logger.
|
To avoid this, system events never cause system events and instead just issue a warning to the logger.
|
||||||
|
|
||||||
## Installation
|
## Debugging
|
||||||
|
|
||||||
Event Bus is available in Maven Central.
|
In more complex setups, taking a look at the event handler execution order can be helpful for debugging.
|
||||||
To include it inside your project, just add the following dependency to your `pom.xml`:
|
Event Bus offers a method for this purpose which can be used as follows:
|
||||||
|
|
||||||
```xml
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>dev.kske</groupId>
|
|
||||||
<artifactId>event-bus-core</artifactId>
|
|
||||||
<version>1.1.0</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, require the Event Bus Core module in your `module-info.java`:
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
requires dev.kske.eventbus.core;
|
System.out.println(EventBus.getInstance().debugExecutionOrder(SimpleEvent.class));
|
||||||
```
|
```
|
||||||
|
|
||||||
If you intend to use event handlers that are inaccessible to Event Bus by means of Java language access control, make sure to allow reflective access from your module:
|
Then, the execution order can be inspected in the console.
|
||||||
|
|
||||||
```java
|
|
||||||
opens my.module to dev.kske.eventbus.core;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Compile-Time Error Checking with Event Bus Proc
|
## Compile-Time Error Checking with Event Bus Proc
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-api</artifactId>
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
<version>5.6.2</version>
|
<version>5.8.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
@ -26,5 +26,14 @@
|
|||||||
<!-- Disable resource folder -->
|
<!-- Disable resource folder -->
|
||||||
<resources />
|
<resources />
|
||||||
|
|
||||||
|
<!-- Run unit tests -->
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>3.0.0-M5</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
@ -22,7 +22,7 @@ import java.lang.annotation.*;
|
|||||||
public @interface Event {
|
public @interface Event {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the event type the handler listens to. If this value is set, the handler is not
|
* Defines the event type the handler listens for. If this value is set, the handler is not
|
||||||
* allowed to declare parameters.
|
* allowed to declare parameters.
|
||||||
* <p>
|
* <p>
|
||||||
* This is useful when the event handler does not utilize the event instance.
|
* This is useful when the event handler does not utilize the event instance.
|
||||||
|
@ -5,6 +5,9 @@ import java.lang.System.Logger.Level;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import dev.kske.eventbus.core.handler.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event listeners can be registered at an event bus to be notified when an event is dispatched.
|
* Event listeners can be registered at an event bus to be notified when an event is dispatched.
|
||||||
@ -27,7 +30,21 @@ public final class EventBus {
|
|||||||
*/
|
*/
|
||||||
private static final class DispatchState {
|
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,32 +55,54 @@ public final class EventBus {
|
|||||||
*/
|
*/
|
||||||
public static final int DEFAULT_PRIORITY = 100;
|
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());
|
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.
|
* Compares event handlers based on priority, but uses hash codes for equal priorities.
|
||||||
*
|
*
|
||||||
* @return a singleton instance of the event bus.
|
* @implNote As the priority comparator by itself is not consistent with equals (two handlers
|
||||||
|
* with the same priority are not necessarily equal, but would have a comparison
|
||||||
|
* result of 0), the hash code is used for the fallback comparison. This way,
|
||||||
|
* consistency with equals is restored.
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
private static final Comparator<EventHandler> byPriority =
|
||||||
|
Comparator.comparingInt(EventHandler::getPriority).reversed()
|
||||||
|
.thenComparingInt(EventHandler::hashCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default event bus, which is a statically initialized singleton instance.
|
||||||
|
*
|
||||||
|
* @return the default event bus
|
||||||
* @since 0.0.2
|
* @since 0.0.2
|
||||||
*/
|
*/
|
||||||
public static EventBus getInstance() {
|
public static EventBus getInstance() {
|
||||||
EventBus instance = singletonInstance;
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<Class<?>, TreeSet<EventHandler>> bindings =
|
/**
|
||||||
new ConcurrentHashMap<>();
|
* Event handler bindings (target class to handlers registered for that class), does not contain
|
||||||
private final Set<Object> registeredListeners =
|
* other (polymorphic) handlers.
|
||||||
ConcurrentHashMap.newKeySet();
|
*
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
private final Map<Class<?>, TreeSet<EventHandler>> bindings = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores all registered event listeners (which declare event handlers) and prevents them from
|
||||||
|
* being garbage collected.
|
||||||
|
*
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
private final Set<Object> registeredListeners = ConcurrentHashMap.newKeySet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current event dispatching state, local to each thread.
|
||||||
|
*
|
||||||
|
* @since 0.1.0
|
||||||
|
*/
|
||||||
private final ThreadLocal<DispatchState> dispatchState =
|
private final ThreadLocal<DispatchState> dispatchState =
|
||||||
ThreadLocal.withInitial(DispatchState::new);
|
ThreadLocal.withInitial(DispatchState::new);
|
||||||
|
|
||||||
@ -73,17 +112,20 @@ public final class EventBus {
|
|||||||
*
|
*
|
||||||
* @param event the event to dispatch
|
* @param event the event to dispatch
|
||||||
* @throws EventBusException if an event handler isn't accessible or has an invalid signature
|
* @throws EventBusException if an event handler isn't accessible or has an invalid signature
|
||||||
|
* @throws NullPointerException if the specified event is {@code null}
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
public void dispatch(Object event) throws EventBusException {
|
public void dispatch(Object event) throws EventBusException {
|
||||||
Objects.requireNonNull(event);
|
Objects.requireNonNull(event);
|
||||||
logger.log(Level.INFO, "Dispatching event {0}", event);
|
logger.log(Level.INFO, "Dispatching event {0}", event);
|
||||||
|
|
||||||
// Set dispatch state
|
// Look up dispatch state
|
||||||
var state = dispatchState.get();
|
var state = dispatchState.get();
|
||||||
state.isDispatching = true;
|
|
||||||
|
|
||||||
Iterator<EventHandler> handlers = getHandlersFor(event.getClass());
|
// Increment nesting count (becomes > 1 during nested dispatches)
|
||||||
|
++state.nestingCount;
|
||||||
|
|
||||||
|
Iterator<EventHandler> handlers = getHandlersFor(event.getClass()).iterator();
|
||||||
if (handlers.hasNext()) {
|
if (handlers.hasNext()) {
|
||||||
while (handlers.hasNext())
|
while (handlers.hasNext())
|
||||||
if (state.isCancelled) {
|
if (state.isCancelled) {
|
||||||
@ -94,14 +136,14 @@ public final class EventBus {
|
|||||||
try {
|
try {
|
||||||
handlers.next().execute(event);
|
handlers.next().execute(event);
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
if (event instanceof DeadEvent || event instanceof ExceptionEvent)
|
if (e.getCause() instanceof Error)
|
||||||
|
|
||||||
// Warn about system event not being handled
|
|
||||||
logger.log(Level.WARNING, event + " not handled due to exception", e);
|
|
||||||
else if (e.getCause() instanceof Error)
|
|
||||||
|
|
||||||
// Transparently pass error to the caller
|
// Transparently pass error to the caller
|
||||||
throw (Error) e.getCause();
|
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
|
else
|
||||||
|
|
||||||
// Dispatch exception event
|
// Dispatch exception event
|
||||||
@ -118,8 +160,8 @@ public final class EventBus {
|
|||||||
dispatch(new DeadEvent(this, event));
|
dispatch(new DeadEvent(this, event));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset dispatch state
|
// Decrement nesting count (becomes 0 when all dispatches on the thread are finished)
|
||||||
state.isDispatching = false;
|
--state.nestingCount;
|
||||||
|
|
||||||
logger.log(Level.DEBUG, "Finished dispatching event {0}", event);
|
logger.log(Level.DEBUG, "Finished dispatching event {0}", event);
|
||||||
}
|
}
|
||||||
@ -128,23 +170,24 @@ public final class EventBus {
|
|||||||
* Searches for the event handlers bound to an event class. This includes polymorphic handlers
|
* Searches for the event handlers bound to an event class. This includes polymorphic handlers
|
||||||
* that are bound to a supertype of the event class.
|
* that are bound to a supertype of the event class.
|
||||||
*
|
*
|
||||||
* @param eventClass the event class to use for the search
|
* @param eventType the event type to use for the search
|
||||||
* @return an iterator over the applicable handlers in descending order of priority
|
* @return a navigable set containing the applicable handlers in descending order of priority
|
||||||
* @since 0.0.1
|
* @since 1.2.0
|
||||||
*/
|
*/
|
||||||
private Iterator<EventHandler> getHandlersFor(Class<?> eventClass) {
|
private NavigableSet<EventHandler> getHandlersFor(Class<?> eventType) {
|
||||||
|
|
||||||
// Get handlers defined for the event class
|
// Get handlers defined for the event class
|
||||||
TreeSet<EventHandler> handlers = bindings.getOrDefault(eventClass, new TreeSet<>());
|
TreeSet<EventHandler> handlers =
|
||||||
|
bindings.getOrDefault(eventType, new TreeSet<>(byPriority));
|
||||||
|
|
||||||
// Get polymorphic handlers
|
// Get polymorphic handlers
|
||||||
for (var binding : bindings.entrySet())
|
for (var binding : bindings.entrySet())
|
||||||
if (binding.getKey().isAssignableFrom(eventClass))
|
if (binding.getKey().isAssignableFrom(eventType))
|
||||||
for (var handler : binding.getValue())
|
for (var handler : binding.getValue())
|
||||||
if (handler.isPolymorphic())
|
if (handler.isPolymorphic())
|
||||||
handlers.add(handler);
|
handlers.add(handler);
|
||||||
|
|
||||||
return handlers.iterator();
|
return handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -155,7 +198,7 @@ public final class EventBus {
|
|||||||
*/
|
*/
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
var state = dispatchState.get();
|
var state = dispatchState.get();
|
||||||
if (state.isDispatching && !state.isCancelled)
|
if (state.nestingCount > 0 && !state.isCancelled)
|
||||||
state.isCancelled = true;
|
state.isCancelled = true;
|
||||||
else
|
else
|
||||||
throw new EventBusException("Calling thread not an active dispatching thread!");
|
throw new EventBusException("Calling thread not an active dispatching thread!");
|
||||||
@ -165,8 +208,9 @@ public final class EventBus {
|
|||||||
* Registers an event listener at this event bus.
|
* Registers an event listener at this event bus.
|
||||||
*
|
*
|
||||||
* @param listener the listener to register
|
* @param listener the listener to register
|
||||||
* @throws EventBusException if the listener is already registered or a declared event handler
|
* @throws EventBusException if the listener is already registered or a declared event
|
||||||
* does not comply with the specification
|
* handler does not comply with the specification
|
||||||
|
* @throws NullPointerException if the specified listener is {@code null}
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
* @see Event
|
* @see Event
|
||||||
*/
|
*/
|
||||||
@ -196,11 +240,8 @@ public final class EventBus {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Initialize and bind the handler
|
// Initialize and bind the handler
|
||||||
var handler = new EventHandler(listener, method, annotation, polymorphic, priority);
|
bindHandler(
|
||||||
bindings.putIfAbsent(handler.getEventType(), new TreeSet<>());
|
new ReflectiveEventHandler(listener, method, annotation, polymorphic, priority));
|
||||||
logger.log(Level.DEBUG, "Binding event handler {0}", handler);
|
|
||||||
bindings.get(handler.getEventType())
|
|
||||||
.add(handler);
|
|
||||||
handlerBound = true;
|
handlerBound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,6 +252,86 @@ public final class EventBus {
|
|||||||
listener.getClass().getName());
|
listener.getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a callback listener, which is a consumer that is invoked when an event occurs. The
|
||||||
|
* listener is not polymorphic and has the {@link #DEFAULT_PRIORITY}.
|
||||||
|
*
|
||||||
|
* @param <E> the event type the listener listens for
|
||||||
|
* @param eventType the event type the listener listens for
|
||||||
|
* @param eventListener the callback that is invoked when an event occurs
|
||||||
|
* @since 1.2.0
|
||||||
|
* @see #registerListener(Class, Consumer, boolean, int)
|
||||||
|
*/
|
||||||
|
public <E> void registerListener(Class<E> eventType, Consumer<E> eventListener) {
|
||||||
|
registerListener(eventType, eventListener, false, DEFAULT_PRIORITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a callback listener, which is a consumer that is invoked when an event occurs. The
|
||||||
|
* listener has the {@link #DEFAULT_PRIORITY}.
|
||||||
|
*
|
||||||
|
* @param <E> the event type the listener listens for
|
||||||
|
* @param eventType the event type the listener listens for
|
||||||
|
* @param eventListener the callback that is invoked when an event occurs
|
||||||
|
* @param polymorphic whether the listener is also invoked for subtypes of the event type
|
||||||
|
* @since 1.2.0
|
||||||
|
* @see #registerListener(Class, Consumer, boolean, int)
|
||||||
|
*/
|
||||||
|
public <E> void registerListener(Class<E> eventType, Consumer<E> eventListener,
|
||||||
|
boolean polymorphic) {
|
||||||
|
registerListener(eventType, eventListener, polymorphic, DEFAULT_PRIORITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a callback listener, which is a consumer that is invoked when an event occurs. The
|
||||||
|
* listener is not polymorphic.
|
||||||
|
*
|
||||||
|
* @param <E> the event type the listener listens for
|
||||||
|
* @param eventType the event type the listener listens for
|
||||||
|
* @param eventListener the callback that is invoked when an event occurs
|
||||||
|
* @param priority the priority to assign to the listener
|
||||||
|
* @since 1.2.0
|
||||||
|
* @see #registerListener(Class, Consumer, boolean, int)
|
||||||
|
*/
|
||||||
|
public <E> void registerListener(Class<E> eventType, Consumer<E> eventListener, int priority) {
|
||||||
|
registerListener(eventType, eventListener, false, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a callback listener, which is a consumer that is invoked when an event occurs.
|
||||||
|
*
|
||||||
|
* @param <E> the event type the listener listens for
|
||||||
|
* @param eventType the event type the listener listens for
|
||||||
|
* @param eventListener the callback that is invoked when an event occurs
|
||||||
|
* @param polymorphic whether the listener is also invoked for subtypes of the event type
|
||||||
|
* @param priority the priority to assign to the listener
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public <E> void registerListener(Class<E> eventType, Consumer<E> eventListener,
|
||||||
|
boolean polymorphic,
|
||||||
|
int priority) {
|
||||||
|
Objects.requireNonNull(eventListener);
|
||||||
|
if (registeredListeners.contains(eventListener))
|
||||||
|
throw new EventBusException(eventListener + " already registered!");
|
||||||
|
logger.log(Level.INFO, "Registering callback event listener {0}",
|
||||||
|
eventListener.getClass().getName());
|
||||||
|
|
||||||
|
registeredListeners.add(eventListener);
|
||||||
|
bindHandler(new CallbackEventHandler(eventType, eventListener, polymorphic, priority));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a new handler into the {@link #bindings} map.
|
||||||
|
*
|
||||||
|
* @param handler the handler to bind
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
private void bindHandler(EventHandler handler) {
|
||||||
|
bindings.putIfAbsent(handler.getEventType(), new TreeSet<>(byPriority));
|
||||||
|
logger.log(Level.DEBUG, "Binding event handler {0}", handler);
|
||||||
|
bindings.get(handler.getEventType()).add(handler);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a specific listener from this event bus.
|
* Removes a specific listener from this event bus.
|
||||||
*
|
*
|
||||||
@ -221,6 +342,7 @@ public final class EventBus {
|
|||||||
Objects.requireNonNull(listener);
|
Objects.requireNonNull(listener);
|
||||||
logger.log(Level.INFO, "Removing event listener {0}", listener.getClass().getName());
|
logger.log(Level.INFO, "Removing event listener {0}", listener.getClass().getName());
|
||||||
|
|
||||||
|
// Remove bindings from binding map
|
||||||
for (var binding : bindings.values()) {
|
for (var binding : bindings.values()) {
|
||||||
var it = binding.iterator();
|
var it = binding.iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
@ -231,6 +353,8 @@ public final class EventBus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove the listener itself
|
||||||
registeredListeners.remove(listener);
|
registeredListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,6 +369,39 @@ public final class EventBus {
|
|||||||
registeredListeners.clear();
|
registeredListeners.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a string describing the event handlers that would be executed for a specific event
|
||||||
|
* type, in order and without actually executing them.
|
||||||
|
*
|
||||||
|
* @apiNote Using this method is only recommended for debugging purposes, as the output depends
|
||||||
|
* on implementation internals which may be subject to change.
|
||||||
|
* @implNote Nested dispatches are not accounted for, as this would require actually executing
|
||||||
|
* the handlers.
|
||||||
|
* @param eventType the event type to generate the execution order for
|
||||||
|
* @return a human-readable event handler list suitable for debugging purposes
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public String debugExecutionOrder(Class<?> eventType) {
|
||||||
|
var handlers = getHandlersFor(eventType);
|
||||||
|
var sj = new StringJoiner("\n");
|
||||||
|
|
||||||
|
// Output header line
|
||||||
|
sj.add(String.format("Event handler execution order for %s (%d handler(s)):", eventType,
|
||||||
|
handlers.size()));
|
||||||
|
sj.add(
|
||||||
|
"==========================================================================================");
|
||||||
|
|
||||||
|
// Individual handlers
|
||||||
|
for (var handler : handlers)
|
||||||
|
sj.add(handler.toString());
|
||||||
|
|
||||||
|
// Bottom line
|
||||||
|
sj.add(
|
||||||
|
"==========================================================================================");
|
||||||
|
|
||||||
|
return sj.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an unmodifiable view of the event listeners registered at this event bus.
|
* Provides an unmodifiable view of the event listeners registered at this event bus.
|
||||||
*
|
*
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
package dev.kske.eventbus.core;
|
package dev.kske.eventbus.core;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This runtime exception is thrown when an event bus error occurs. This can
|
* This unchecked exception is specific to the event bus and can be thrown under the following
|
||||||
* either occur while registering event listeners with invalid handlers, or when
|
* circumstances:
|
||||||
* an event handler throws an exception.
|
* <ul>
|
||||||
|
* <li>An event handler throws an exception (which is stored as the cause)</li>
|
||||||
|
* <li>An event listener with an invalid event handler is registered</li>
|
||||||
|
* <li>{@link EventBus#cancel()} is invoked from outside an active dispatch thread</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Kai S. K. Engelbart
|
* @author Kai S. K. Engelbart
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
public class EventBusException extends RuntimeException {
|
public final class EventBusException extends RuntimeException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
package dev.kske.eventbus.core.handler;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event handler wrapping a callback method.
|
||||||
|
*
|
||||||
|
* @author Kai S. K. Engelbart
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public final class CallbackEventHandler implements EventHandler {
|
||||||
|
|
||||||
|
private final Class<?> eventType;
|
||||||
|
private final Consumer<Object> callback;
|
||||||
|
private final boolean polymorphic;
|
||||||
|
private final int priority;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a callback event handler.
|
||||||
|
*
|
||||||
|
* @param <E> the event type of the handler
|
||||||
|
* @param eventType the event type of the handler
|
||||||
|
* @param callback the callback method to execute when the handler is invoked
|
||||||
|
* @param polymorphic whether the handler is polymorphic
|
||||||
|
* @param priority the priority of the handler
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <E> CallbackEventHandler(Class<E> eventType, Consumer<E> callback, boolean polymorphic,
|
||||||
|
int priority) {
|
||||||
|
this.eventType = eventType;
|
||||||
|
this.callback = (Consumer<Object>) callback;
|
||||||
|
this.polymorphic = polymorphic;
|
||||||
|
this.priority = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Object event) throws InvocationTargetException {
|
||||||
|
try {
|
||||||
|
callback.accept(event);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw new InvocationTargetException(e, "Callback event handler failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format(
|
||||||
|
"CallbackEventHandler[eventType=%s, polymorphic=%b, priority=%d]",
|
||||||
|
eventType, polymorphic, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Consumer<?> getListener() {
|
||||||
|
return callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getEventType() {
|
||||||
|
return eventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPolymorphic() {
|
||||||
|
return polymorphic;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package dev.kske.eventbus.core.handler;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
import dev.kske.eventbus.core.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal representation of an event handling method.
|
||||||
|
*
|
||||||
|
* @author Kai S. K. Engelbart
|
||||||
|
* @since 1.2.0
|
||||||
|
* @see EventBus
|
||||||
|
*/
|
||||||
|
public interface EventHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the event handler.
|
||||||
|
*
|
||||||
|
* @param event the event used as the method parameter
|
||||||
|
* @throws EventBusException if the event handler isn't accessible or has an invalid
|
||||||
|
* signature
|
||||||
|
* @throws InvocationTargetException if the handler throws an exception
|
||||||
|
* @throws EventBusException if the handler has the wrong signature or is inaccessible
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
void execute(Object event) throws EventBusException, InvocationTargetException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the listener containing this handler
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
Object getListener();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the event type this handler listens for
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
Class<?> getEventType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the priority of this handler
|
||||||
|
* @since 1.2.0
|
||||||
|
* @see Priority
|
||||||
|
*/
|
||||||
|
int getPriority();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether this handler also accepts subtypes of the event type
|
||||||
|
* @since 1.2.0
|
||||||
|
* @see Polymorphic
|
||||||
|
*/
|
||||||
|
boolean isPolymorphic();
|
||||||
|
}
|
@ -1,17 +1,18 @@
|
|||||||
package dev.kske.eventbus.core;
|
package dev.kske.eventbus.core.handler;
|
||||||
|
|
||||||
import java.lang.reflect.*;
|
import java.lang.reflect.*;
|
||||||
|
|
||||||
|
import dev.kske.eventbus.core.*;
|
||||||
import dev.kske.eventbus.core.Event.USE_PARAMETER;
|
import dev.kske.eventbus.core.Event.USE_PARAMETER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal representation of an event handling method.
|
* An event handler wrapping a method annotated with {@link Event} and executing it using
|
||||||
|
* reflection.
|
||||||
*
|
*
|
||||||
* @author Kai S. K. Engelbart
|
* @author Kai S. K. Engelbart
|
||||||
* @since 0.0.1
|
* @since 1.2.0
|
||||||
* @see EventBus
|
|
||||||
*/
|
*/
|
||||||
final class EventHandler implements Comparable<EventHandler> {
|
public final class ReflectiveEventHandler implements EventHandler {
|
||||||
|
|
||||||
private final Object listener;
|
private final Object listener;
|
||||||
private final Method method;
|
private final Method method;
|
||||||
@ -21,7 +22,7 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
private final int priority;
|
private final int priority;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an event handler.
|
* Constructs a reflective event handler.
|
||||||
*
|
*
|
||||||
* @param listener the listener containing the handler
|
* @param listener the listener containing the handler
|
||||||
* @param method the handler method
|
* @param method the handler method
|
||||||
@ -30,10 +31,10 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
* @param defPriority the predefined priority (default or listener-level)
|
* @param defPriority the predefined priority (default or listener-level)
|
||||||
* @throws EventBusException if the method or the annotation do not comply with the
|
* @throws EventBusException if the method or the annotation do not comply with the
|
||||||
* specification
|
* specification
|
||||||
* @since 0.0.1
|
* @since 1.2.0
|
||||||
*/
|
*/
|
||||||
EventHandler(Object listener, Method method, Event annotation, boolean defPolymorphism,
|
public ReflectiveEventHandler(Object listener, Method method, Event annotation,
|
||||||
int defPriority) throws EventBusException {
|
boolean defPolymorphism, int defPriority) throws EventBusException {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.method = method;
|
this.method = method;
|
||||||
useParameter = annotation.value() == USE_PARAMETER.class;
|
useParameter = annotation.value() == USE_PARAMETER.class;
|
||||||
@ -61,45 +62,13 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares this to another event handler based on priority. In case of equal priority a
|
|
||||||
* non-zero value based on hash codes is returned.
|
|
||||||
* <p>
|
|
||||||
* This is used to retrieve event handlers in descending order of priority from a tree set.
|
|
||||||
*
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(EventHandler other) {
|
public void execute(Object event) throws EventBusException, InvocationTargetException {
|
||||||
int priority = other.priority - this.priority;
|
|
||||||
if (priority == 0)
|
|
||||||
priority = listener.hashCode() - other.listener.hashCode();
|
|
||||||
return priority == 0 ? hashCode() - other.hashCode() : priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.format(
|
|
||||||
"EventHandler[method=%s, eventType=%s, useParameter=%b, polymorphic=%b, priority=%d]",
|
|
||||||
method, eventType, useParameter, polymorphic, priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the event handler.
|
|
||||||
*
|
|
||||||
* @param event the event used as the method parameter
|
|
||||||
* @throws EventBusException if the event handler isn't accessible or has an invalid
|
|
||||||
* signature
|
|
||||||
* @throws InvocationTargetException if the handler throws an exception
|
|
||||||
* @throws EventBusException if the handler has the wrong signature or is inaccessible
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
void execute(Object event) throws EventBusException, InvocationTargetException {
|
|
||||||
try {
|
try {
|
||||||
if (useParameter)
|
if (useParameter)
|
||||||
method.invoke(listener, event);
|
method.invoke(getListener(), event);
|
||||||
else
|
else
|
||||||
method.invoke(listener);
|
method.invoke(getListener());
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new EventBusException("Event handler rejected target / argument!", e);
|
throw new EventBusException("Event handler rejected target / argument!", e);
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
@ -107,29 +76,30 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* @return the listener containing this handler
|
public String toString() {
|
||||||
* @since 0.0.1
|
return String.format(
|
||||||
*/
|
"ReflectiveEventHandler[eventType=%s, polymorphic=%b, priority=%d, method=%s, useParameter=%b]",
|
||||||
Object getListener() { return listener; }
|
eventType, polymorphic, priority, method, useParameter);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* @return the event type this handler listens for
|
public Object getListener() {
|
||||||
* @since 0.0.3
|
return listener;
|
||||||
*/
|
}
|
||||||
Class<?> getEventType() { return eventType; }
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* @return the priority of this handler
|
public Class<?> getEventType() {
|
||||||
* @since 0.0.1
|
return eventType;
|
||||||
* @see Priority
|
}
|
||||||
*/
|
|
||||||
int getPriority() { return priority; }
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* @return whether this handler is polymorphic
|
public int getPriority() {
|
||||||
* @since 1.0.0
|
return priority;
|
||||||
* @see Polymorphic
|
}
|
||||||
*/
|
|
||||||
boolean isPolymorphic() { return polymorphic; }
|
@Override
|
||||||
|
public boolean isPolymorphic() {
|
||||||
|
return polymorphic;
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* Contains the internal representation of event handling methods.
|
||||||
|
*
|
||||||
|
* @author Kai S. K. Engelbart
|
||||||
|
* @since 1.2.0
|
||||||
|
* @see dev.kske.eventbus.core.handler.EventHandler
|
||||||
|
*/
|
||||||
|
package dev.kske.eventbus.core.handler;
|
@ -11,7 +11,7 @@ import org.junit.jupiter.api.*;
|
|||||||
* @author Leon Hofmeister
|
* @author Leon Hofmeister
|
||||||
* @since 0.1.0
|
* @since 0.1.0
|
||||||
*/
|
*/
|
||||||
class CancelTest {
|
public class CancelTest {
|
||||||
|
|
||||||
EventBus bus;
|
EventBus bus;
|
||||||
int hits;
|
int hits;
|
||||||
@ -22,7 +22,7 @@ class CancelTest {
|
|||||||
* @since 0.1.0
|
* @since 0.1.0
|
||||||
*/
|
*/
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void registerListener() {
|
public void registerListener() {
|
||||||
bus = new EventBus();
|
bus = new EventBus();
|
||||||
bus.registerListener(this);
|
bus.registerListener(this);
|
||||||
}
|
}
|
||||||
@ -34,7 +34,7 @@ class CancelTest {
|
|||||||
* @since 0.1.0
|
* @since 0.1.0
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testCancellation() {
|
public void testCancellation() {
|
||||||
bus.dispatch(new SimpleEvent());
|
bus.dispatch(new SimpleEvent());
|
||||||
assertEquals(1, hits);
|
assertEquals(1, hits);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
* @author Kai S. K. Engelbart
|
* @author Kai S. K. Engelbart
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
class DeadTest {
|
public class DeadTest {
|
||||||
|
|
||||||
EventBus bus = new EventBus();
|
EventBus bus = new EventBus();
|
||||||
String event = "This event has no handler";
|
String event = "This event has no handler";
|
||||||
@ -22,7 +22,7 @@ class DeadTest {
|
|||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testDeadEvent() {
|
public void testDeadEvent() {
|
||||||
bus.registerListener(this);
|
bus.registerListener(this);
|
||||||
bus.dispatch(event);
|
bus.dispatch(event);
|
||||||
assertTrue(deadEventHandled);
|
assertTrue(deadEventHandled);
|
||||||
@ -36,7 +36,7 @@ class DeadTest {
|
|||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testUnhandledDeadEvent() {
|
public void testUnhandledDeadEvent() {
|
||||||
bus.dispatch(event);
|
bus.dispatch(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import org.junit.jupiter.api.*;
|
|||||||
*/
|
*/
|
||||||
@Polymorphic
|
@Polymorphic
|
||||||
@Priority(150)
|
@Priority(150)
|
||||||
class DispatchTest {
|
public class DispatchTest {
|
||||||
|
|
||||||
EventBus bus;
|
EventBus bus;
|
||||||
static int hits;
|
static int hits;
|
||||||
@ -23,9 +23,13 @@ class DispatchTest {
|
|||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void registerListener() {
|
public void registerListener() {
|
||||||
bus = new EventBus();
|
bus = new EventBus();
|
||||||
bus.registerListener(this);
|
bus.registerListener(this);
|
||||||
|
bus.registerListener(SimpleEvent.class, e -> {
|
||||||
|
++hits;
|
||||||
|
assertEquals(4, hits);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,11 +39,30 @@ class DispatchTest {
|
|||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testDispatch() {
|
public void testDispatch() {
|
||||||
bus.dispatch(new SimpleEventSub());
|
bus.dispatch(new SimpleEventSub());
|
||||||
bus.dispatch(new SimpleEvent());
|
bus.dispatch(new SimpleEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link EventBus#debugExecutionOrder(Class)} based on the currently registered handlers.
|
||||||
|
*
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testDebugExecutionOrder() {
|
||||||
|
String executionOrder = bus.debugExecutionOrder(SimpleEvent.class);
|
||||||
|
System.out.println(executionOrder);
|
||||||
|
assertEquals(
|
||||||
|
"Event handler execution order for class dev.kske.eventbus.core.SimpleEvent (3 handler(s)):\n"
|
||||||
|
+ "==========================================================================================\n"
|
||||||
|
+ "ReflectiveEventHandler[eventType=class dev.kske.eventbus.core.SimpleEvent, polymorphic=true, priority=200, method=void dev.kske.eventbus.core.DispatchTest.onSimpleEventFirst(), useParameter=false]\n"
|
||||||
|
+ "ReflectiveEventHandler[eventType=class dev.kske.eventbus.core.SimpleEvent, polymorphic=false, priority=150, method=static void dev.kske.eventbus.core.DispatchTest.onSimpleEventSecond(), useParameter=false]\n"
|
||||||
|
+ "CallbackEventHandler[eventType=class dev.kske.eventbus.core.SimpleEvent, polymorphic=false, priority=100]\n"
|
||||||
|
+ "==========================================================================================",
|
||||||
|
executionOrder);
|
||||||
|
}
|
||||||
|
|
||||||
@Event(SimpleEvent.class)
|
@Event(SimpleEvent.class)
|
||||||
@Priority(200)
|
@Priority(200)
|
||||||
void onSimpleEventFirst() {
|
void onSimpleEventFirst() {
|
||||||
@ -53,12 +76,4 @@ class DispatchTest {
|
|||||||
++hits;
|
++hits;
|
||||||
assertEquals(3, hits);
|
assertEquals(3, hits);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Event
|
|
||||||
@Polymorphic(false)
|
|
||||||
@Priority(100)
|
|
||||||
void onSimpleEventThird(SimpleEvent event) {
|
|
||||||
++hits;
|
|
||||||
assertEquals(4, hits);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
* @author Kai S. K. Engelbart
|
* @author Kai S. K. Engelbart
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
class ExceptionTest {
|
public class ExceptionTest {
|
||||||
|
|
||||||
EventBus bus = new EventBus();
|
EventBus bus = new EventBus();
|
||||||
String event = "This event will cause an exception";
|
String event = "This event will cause an exception";
|
||||||
@ -23,7 +23,7 @@ class ExceptionTest {
|
|||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testExceptionEvent() {
|
public void testExceptionEvent() {
|
||||||
bus.registerListener(this);
|
bus.registerListener(this);
|
||||||
bus.registerListener(new ExceptionListener());
|
bus.registerListener(new ExceptionListener());
|
||||||
bus.dispatch(event);
|
bus.dispatch(event);
|
||||||
@ -38,7 +38,7 @@ class ExceptionTest {
|
|||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testUnhandledExceptionEvent() {
|
public void testUnhandledExceptionEvent() {
|
||||||
bus.registerListener(this);
|
bus.registerListener(this);
|
||||||
bus.dispatch(event);
|
bus.dispatch(event);
|
||||||
bus.removeListener(this);
|
bus.removeListener(this);
|
||||||
|
@ -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
|
||||||
|
*/
|
||||||
|
public class NestedTest {
|
||||||
|
|
||||||
|
EventBus bus;
|
||||||
|
boolean nestedHit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an event bus and registers this test instance as an event listener.
|
||||||
|
*
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@BeforeEach
|
||||||
|
public 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
|
||||||
|
public 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());
|
||||||
|
}
|
||||||
|
}
|
24
pom.xml
24
pom.xml
@ -9,7 +9,7 @@
|
|||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<name>Event Bus</name>
|
<name>Event Bus</name>
|
||||||
<description>An event handling framework for Java utilizing annotations.</description>
|
<description>An event handling library for Java utilizing annotations.</description>
|
||||||
<url>https://git.kske.dev/kske/event-bus</url>
|
<url>https://git.kske.dev/kske/event-bus</url>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
@ -120,6 +120,28 @@
|
|||||||
</goals>
|
</goals>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
|
|
||||||
|
<!-- Support JDK-style Javadoc tags -->
|
||||||
|
<configuration>
|
||||||
|
<tags>
|
||||||
|
<tag>
|
||||||
|
<name>apiNote</name>
|
||||||
|
<placement>a</placement>
|
||||||
|
<head>API Note:</head>
|
||||||
|
</tag>
|
||||||
|
<tag>
|
||||||
|
<name>implSpec</name>
|
||||||
|
<placement>a</placement>
|
||||||
|
<head>Implementation Requirements:</head>
|
||||||
|
</tag>
|
||||||
|
<tag>
|
||||||
|
<name>implNote</name>
|
||||||
|
<placement>a</placement>
|
||||||
|
<head>Implementation Note:</head>
|
||||||
|
</tag>
|
||||||
|
</tags>
|
||||||
|
</configuration>
|
||||||
|
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
<!-- GPG sign JAR -->
|
<!-- GPG sign JAR -->
|
||||||
|
Reference in New Issue
Block a user