Merge pull request 'Exception Wrapper' (#32) from f/exception-wrapper into develop
Reviewed-on: https://git.kske.dev/kske/event-bus/pulls/32 Reviewed-by: delvh <leon@kske.dev> Reviewed-by: DieGurke <maxi@kske.dev>
This commit is contained in:
commit
27d14a844d
27
README.md
27
README.md
@ -183,14 +183,37 @@ private void onDeadEvent(DeadEvent deadEvent) { ... }
|
|||||||
### Detecting Exceptions Thrown by Event Handlers
|
### Detecting Exceptions Thrown by Event Handlers
|
||||||
|
|
||||||
When an event handler throws an exception, an exception event is dispatched that wraps the original event.
|
When an event handler throws an exception, an exception event is dispatched that wraps the original event.
|
||||||
A exception handler is declared as follows:
|
An exception handler is declared as follows:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
private void onExceptionEvent(ExceptionEvent ExceptionEvent) { ... }
|
private void onExceptionEvent(ExceptionEvent ExceptionEvent) { ... }
|
||||||
```
|
```
|
||||||
|
|
||||||
Both system events reference the event bus that caused them and a warning is logged if they are unhandled.
|
Both system events reference the event bus that caused them and a warning is logged if they are unhandled.
|
||||||
|
|
||||||
|
#### Yeeting Exceptions Out of an Event Handler
|
||||||
|
|
||||||
|
In some cases, a warning about an `Exception` that was thrown in an event handler is not enough, stays unnoticed, or an exception should be catched explicitly.
|
||||||
|
Event Bus explicitly dispatches no `ExceptionEvent` when an `ExceptionWrapper` exception is thrown and instead simply rethrows it.
|
||||||
|
`ExceptionWrapper` is an unchecked exception that (as the name says) simply wraps an exception that caused it.
|
||||||
|
This means the following is possible and results in a normal program exit:
|
||||||
|
```java
|
||||||
|
@Event(String.class)
|
||||||
|
void onString() {
|
||||||
|
throw new ExceptionWrapper(new RuntimeException("I failed!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void helloStackTrace() {
|
||||||
|
EventBus.getInstance().registerListener(this);
|
||||||
|
try {
|
||||||
|
EventBus.getInstance().dispatch("A string!");
|
||||||
|
System.exit(-1);
|
||||||
|
} catch(ExceptionWrapper e) {
|
||||||
|
e.getCause().printStackTrace();
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### What About Endless Recursion Caused By Dead Events and Exception Events?
|
### 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.
|
As one might imagine, an unhandled dead event would theoretically lead to an endless recursion.
|
||||||
|
@ -112,10 +112,11 @@ 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 ExceptionWrapper if it is thrown by an event handler
|
||||||
* @throws NullPointerException if the specified event is {@code null}
|
* @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) {
|
||||||
Objects.requireNonNull(event);
|
Objects.requireNonNull(event);
|
||||||
logger.log(Level.INFO, "Dispatching event {0}", event);
|
logger.log(Level.INFO, "Dispatching event {0}", event);
|
||||||
|
|
||||||
@ -140,6 +141,10 @@ public final class EventBus {
|
|||||||
|
|
||||||
// Transparently pass error to the caller
|
// Transparently pass error to the caller
|
||||||
throw (Error) e.getCause();
|
throw (Error) e.getCause();
|
||||||
|
else if (e.getCause() instanceof ExceptionWrapper)
|
||||||
|
|
||||||
|
// Transparently pass exception wrapper to the caller
|
||||||
|
throw (ExceptionWrapper) e.getCause();
|
||||||
else if (event instanceof DeadEvent || event instanceof ExceptionEvent)
|
else if (event instanceof DeadEvent || event instanceof ExceptionEvent)
|
||||||
|
|
||||||
// Warn about system event not being handled
|
// Warn about system event not being handled
|
||||||
@ -214,7 +219,7 @@ public final class EventBus {
|
|||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
* @see Event
|
* @see Event
|
||||||
*/
|
*/
|
||||||
public void registerListener(Object listener) throws EventBusException {
|
public void registerListener(Object listener) {
|
||||||
Objects.requireNonNull(listener);
|
Objects.requireNonNull(listener);
|
||||||
if (registeredListeners.contains(listener))
|
if (registeredListeners.contains(listener))
|
||||||
throw new EventBusException(listener + " already registered!");
|
throw new EventBusException(listener + " already registered!");
|
||||||
|
@ -14,13 +14,14 @@ package dev.kske.eventbus.core;
|
|||||||
*/
|
*/
|
||||||
public final class EventBusException extends RuntimeException {
|
public final class EventBusException extends RuntimeException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 7254445250300604449L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new event bus exception.
|
* Creates a new event bus exception.
|
||||||
*
|
*
|
||||||
* @param message the message to display
|
* @param message the message to display
|
||||||
* @param cause the cause of this exception
|
* @param cause the cause of this exception
|
||||||
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
public EventBusException(String message, Throwable cause) {
|
public EventBusException(String message, Throwable cause) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
@ -30,6 +31,7 @@ public final class EventBusException extends RuntimeException {
|
|||||||
* Creates a new event bus exception.
|
* Creates a new event bus exception.
|
||||||
*
|
*
|
||||||
* @param message the message to display
|
* @param message the message to display
|
||||||
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
public EventBusException(String message) {
|
public EventBusException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package dev.kske.eventbus.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This unchecked exception acts as a wrapper for an arbitrary exception to prevent an
|
||||||
|
* {@link ExceptionEvent} from being dispatched. Instead, the wrapped exception is rethrown by
|
||||||
|
* {@link EventBus#dispatch(Object)}.
|
||||||
|
*
|
||||||
|
* @author Kai S. K. Engelbart
|
||||||
|
* @since 1.2.1
|
||||||
|
*/
|
||||||
|
public final class ExceptionWrapper extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -2016681140617308788L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new exception wrapper.
|
||||||
|
*
|
||||||
|
* @param cause the exception to wrap
|
||||||
|
* @since 1.2.1
|
||||||
|
*/
|
||||||
|
public ExceptionWrapper(Exception cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package dev.kske.eventbus.core;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the behavior of the event bus when an {@link ExceptionWrapper} is thrown.
|
||||||
|
*
|
||||||
|
* @author Kai S. K. Engelbart
|
||||||
|
* @since 1.2.1
|
||||||
|
*/
|
||||||
|
public class ExceptionWrapperTest {
|
||||||
|
|
||||||
|
EventBus bus = new EventBus();
|
||||||
|
String event = "This event will cause an exception";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests transparent rethrowing of an exception wrapper by {@link EventBus#dispatch(Object)}.
|
||||||
|
*
|
||||||
|
* @since 1.2.1
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testExceptionWrapper() {
|
||||||
|
bus.registerListener(this);
|
||||||
|
assertThrows(ExceptionWrapper.class, () -> bus.dispatch(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Event(String.class)
|
||||||
|
void onString() {
|
||||||
|
throw new ExceptionWrapper(new RuntimeException("I failed!"));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user