Merge pull request 'Add ExceptionEvent' (#12) from f/exception-event into develop
Reviewed-on: https://git.kske.dev/kske/event-bus/pulls/12 Reviewed-by: delvh <leon@kske.dev> Reviewed-by: DieGurke <maxi@kske.dev>
This commit is contained in:
commit
d9ddc0e1a9
32
README.md
32
README.md
@ -123,6 +123,38 @@ This applies to all event handlers that would have been executed after the one c
|
||||
Avoid cancelling events while using multiple event handlers with the same priority.
|
||||
As event handlers are ordered by priority, it is not defined which of them will be executed after the event has been consumed.
|
||||
|
||||
## System Events
|
||||
|
||||
To accommodate for special circumstances in an event distribution, system events have been introduced.
|
||||
At the moment, there are two system events, which are explained in this section.
|
||||
|
||||
### Detecting Unhandled Events
|
||||
|
||||
When an event is dispatched but not delivered to any handler, a dead event is dispatched that wraps the original event.
|
||||
You can declare a dead event handler to respond to this situation:
|
||||
|
||||
```java
|
||||
private void onDeadEvent(DeadEvent deadEvent) { ... }
|
||||
```
|
||||
|
||||
### Detecting Exceptions Thrown by Event Handlers
|
||||
|
||||
When an event handler throws an exception, an exception event is dispatched that wraps the original event.
|
||||
A exception handler is declared as follows:
|
||||
|
||||
```java
|
||||
private void onExceptionEvent(ExceptionEvent ExceptionEvent) { ... }
|
||||
```
|
||||
|
||||
Both system events reference the event bus that caused them and a warning is logged if they are unhandled.
|
||||
|
||||
### What About Endless Recursion Caused By Dead Events and Exception Events?
|
||||
|
||||
As one might imagine, an unhandled dead event would theoretically lead to an endless recursion.
|
||||
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.
|
||||
|
||||
## Installation
|
||||
|
||||
Event Bus is available in Maven Central.
|
||||
|
@ -2,6 +2,7 @@ package dev.kske.eventbus.core;
|
||||
|
||||
import java.lang.System.Logger;
|
||||
import java.lang.System.Logger.Level;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@ -63,6 +64,7 @@ public final class EventBus {
|
||||
* priority.
|
||||
*
|
||||
* @param event the event to dispatch
|
||||
* @throws EventBusException if an event handler isn't accessible or has an invalid signature
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public void dispatch(Object event) {
|
||||
@ -81,16 +83,27 @@ public final class EventBus {
|
||||
state.isCancelled = false;
|
||||
break;
|
||||
} else {
|
||||
handlers.next().execute(event);
|
||||
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
|
||||
|
||||
// Dispatch exception event
|
||||
dispatch(new ExceptionEvent(this, event, e.getCause()));
|
||||
}
|
||||
}
|
||||
} else if (!(event instanceof DeadEvent)) {
|
||||
|
||||
// Dispatch dead event
|
||||
dispatch(new DeadEvent(this, event));
|
||||
} else {
|
||||
|
||||
} else if (event instanceof DeadEvent || event instanceof ExceptionEvent) {
|
||||
|
||||
// Warn about the dead event not being handled
|
||||
logger.log(Level.WARNING, "{0} not handled", event);
|
||||
} else {
|
||||
|
||||
// Dispatch dead event
|
||||
dispatch(new DeadEvent(this, event));
|
||||
}
|
||||
|
||||
// Reset dispatch state
|
||||
|
@ -91,17 +91,21 @@ final class EventHandler implements Comparable<EventHandler> {
|
||||
* Executes the event handler.
|
||||
*
|
||||
* @param event the event used as the method parameter
|
||||
* @throws EventBusException if the handler throws an exception
|
||||
* @throws EventBusException if the event handler isn't accessible or has an invalid
|
||||
* signature
|
||||
* @throws InvocationTargetException if the handler throws an exception
|
||||
* @since 0.0.1
|
||||
*/
|
||||
void execute(Object event) throws EventBusException {
|
||||
void execute(Object event) throws EventBusException, InvocationTargetException {
|
||||
try {
|
||||
if (useParameter)
|
||||
method.invoke(listener, event);
|
||||
else
|
||||
method.invoke(listener);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw new EventBusException("Failed to invoke event handler!", e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new EventBusException("Event handler rejected target / argument!", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new EventBusException("Event handler is not accessible!", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,47 @@
|
||||
package dev.kske.eventbus.core;
|
||||
|
||||
/**
|
||||
* Wraps an event that was dispatched but caused an exception in one of its handlers.
|
||||
* <p>
|
||||
* Handling exception events is useful as it allows the creation of a centralized exception handling
|
||||
* mechanism for unexpected exceptions.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public final class ExceptionEvent {
|
||||
|
||||
private final EventBus eventBus;
|
||||
private final Object event;
|
||||
private final Throwable cause;
|
||||
|
||||
ExceptionEvent(EventBus eventBus, Object event, Throwable cause) {
|
||||
this.eventBus = eventBus;
|
||||
this.event = event;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("ExceptionEvent[eventBus=%s, event=%s, cause=%s]", eventBus, event,
|
||||
cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the event bus that dispatched this event
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public EventBus getEventBus() { return eventBus; }
|
||||
|
||||
/**
|
||||
* @return the event that could not be handled because of an exception
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public Object getEvent() { return event; }
|
||||
|
||||
/**
|
||||
* @return the exception that was thrown while handling the event
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public Throwable getCause() { return cause; }
|
||||
}
|
@ -31,7 +31,7 @@ class DeadTest {
|
||||
|
||||
/**
|
||||
* Tests how the event bus reacts to an unhandled dead event. This should not lead to an
|
||||
* exception or endless recursion and instead be logged.
|
||||
* exception or an endless recursion and should be logged instead.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
|
@ -0,0 +1,62 @@
|
||||
package dev.kske.eventbus.core;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests the dispatching of an exception event if an event handler threw an exception.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since 1.1.0
|
||||
*/
|
||||
class ExceptionTest {
|
||||
|
||||
EventBus bus = new EventBus();
|
||||
String event = "This event will cause an exception";
|
||||
RuntimeException exception = new RuntimeException("I failed");
|
||||
boolean exceptionEventHandled;
|
||||
|
||||
/**
|
||||
* Tests exception event delivery.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Test
|
||||
void testExceptionEvent() {
|
||||
bus.registerListener(this);
|
||||
bus.registerListener(new ExceptionListener());
|
||||
bus.dispatch(event);
|
||||
assertTrue(exceptionEventHandled);
|
||||
bus.clearListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests how the event bus reacts to an unhandled exception event. This should not lead to an
|
||||
* exception or an endless recursion and should be logged instead.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Test
|
||||
void testUnhandledExceptionEvent() {
|
||||
bus.registerListener(this);
|
||||
bus.dispatch(event);
|
||||
bus.removeListener(this);
|
||||
}
|
||||
|
||||
@Event(String.class)
|
||||
void onString() {
|
||||
throw exception;
|
||||
}
|
||||
|
||||
class ExceptionListener {
|
||||
|
||||
@Event
|
||||
void onExceptionEvent(ExceptionEvent exceptionEvent) {
|
||||
assertEquals(bus, exceptionEvent.getEventBus());
|
||||
assertEquals(event, exceptionEvent.getEvent());
|
||||
assertEquals(exception, exceptionEvent.getCause());
|
||||
exceptionEventHandled = true;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user