Add ExceptionEvent
An exception event wraps an event that caused an exception inside of an event handler while being dispatched and is then dispatched to dedicated handlers.
This commit is contained in:
parent
0f9b64be48
commit
6a2cad4ae5
@ -2,6 +2,7 @@ package dev.kske.eventbus.core;
|
|||||||
|
|
||||||
import java.lang.System.Logger;
|
import java.lang.System.Logger;
|
||||||
import java.lang.System.Logger.Level;
|
import java.lang.System.Logger.Level;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
@ -63,6 +64,7 @@ public final class EventBus {
|
|||||||
* priority.
|
* priority.
|
||||||
*
|
*
|
||||||
* @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
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
public void dispatch(Object event) {
|
public void dispatch(Object event) {
|
||||||
@ -81,16 +83,27 @@ public final class EventBus {
|
|||||||
state.isCancelled = false;
|
state.isCancelled = false;
|
||||||
break;
|
break;
|
||||||
} else {
|
} 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", e);
|
||||||
|
else
|
||||||
|
|
||||||
|
// Dispatch exception event
|
||||||
|
dispatch(new ExceptionEvent(this, event, e.getCause()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (!(event instanceof DeadEvent)) {
|
} else if (event instanceof DeadEvent || event instanceof ExceptionEvent) {
|
||||||
|
|
||||||
// Dispatch dead event
|
|
||||||
dispatch(new DeadEvent(this, event));
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Warn about the dead event not being handled
|
// Warn about the dead event not being handled
|
||||||
logger.log(Level.WARNING, "{0} not handled", event);
|
logger.log(Level.WARNING, "{0} not handled", event);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Dispatch dead event
|
||||||
|
dispatch(new DeadEvent(this, event));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset dispatch state
|
// Reset dispatch state
|
||||||
|
@ -91,17 +91,21 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
* Executes the event handler.
|
* Executes the event handler.
|
||||||
*
|
*
|
||||||
* @param event the event used as the method parameter
|
* @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
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
void execute(Object event) throws EventBusException {
|
void execute(Object event) throws EventBusException, InvocationTargetException {
|
||||||
try {
|
try {
|
||||||
if (useParameter)
|
if (useParameter)
|
||||||
method.invoke(listener, event);
|
method.invoke(listener, event);
|
||||||
else
|
else
|
||||||
method.invoke(listener);
|
method.invoke(listener);
|
||||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new EventBusException("Failed to invoke event handler!", 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
|
* 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
|
* @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