2021-02-09 09:52:26 +01:00
|
|
|
package dev.kske.eventbus.core;
|
2020-09-02 16:15:31 +02:00
|
|
|
|
2020-09-20 15:27:51 +02:00
|
|
|
import java.lang.System.Logger;
|
|
|
|
import java.lang.System.Logger.Level;
|
2021-02-21 10:36:06 +01:00
|
|
|
import java.lang.reflect.InvocationTargetException;
|
2020-09-02 16:15:31 +02:00
|
|
|
import java.util.*;
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
2021-11-02 19:33:18 +01:00
|
|
|
import java.util.function.Consumer;
|
|
|
|
|
|
|
|
import dev.kske.eventbus.core.handler.*;
|
2020-09-02 16:15:31 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Event listeners can be registered at an event bus to be notified when an event is dispatched.
|
|
|
|
* <p>
|
2020-09-06 15:32:54 +02:00
|
|
|
* A singleton instance of this class can be lazily created and acquired using the
|
|
|
|
* {@link EventBus#getInstance()} method.
|
|
|
|
* <p>
|
2020-09-02 16:15:31 +02:00
|
|
|
* This is a thread-safe implementation.
|
|
|
|
*
|
|
|
|
* @author Kai S. K. Engelbart
|
|
|
|
* @since 0.0.1
|
|
|
|
* @see Event
|
|
|
|
*/
|
|
|
|
public final class EventBus {
|
|
|
|
|
2020-10-02 17:50:11 +02:00
|
|
|
/**
|
|
|
|
* Holds the state of the dispatching process on one thread.
|
|
|
|
*
|
|
|
|
* @since 0.1.0
|
|
|
|
*/
|
|
|
|
private static final class DispatchState {
|
|
|
|
|
2021-07-12 10:19:04 +02:00
|
|
|
/**
|
|
|
|
* 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;
|
2020-10-02 17:50:11 +02:00
|
|
|
}
|
|
|
|
|
2021-03-14 11:18:11 +01:00
|
|
|
/**
|
|
|
|
* The priority assigned to every event handler without an explicitly defined priority.
|
|
|
|
*
|
|
|
|
* @since 1.1.0
|
|
|
|
* @see Priority
|
|
|
|
*/
|
|
|
|
public static final int DEFAULT_PRIORITY = 100;
|
|
|
|
|
2021-10-15 08:25:38 +02:00
|
|
|
private static final EventBus singletonInstance = new EventBus();
|
2020-09-06 15:32:54 +02:00
|
|
|
|
2020-09-20 15:27:51 +02:00
|
|
|
private static final Logger logger = System.getLogger(EventBus.class.getName());
|
|
|
|
|
2021-11-02 19:33:18 +01:00
|
|
|
/**
|
|
|
|
* Compares event handlers based on priority, but uses hash codes for equal priorities.
|
|
|
|
*
|
|
|
|
* @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);
|
|
|
|
|
2020-09-06 15:32:54 +02:00
|
|
|
/**
|
2021-10-15 08:25:38 +02:00
|
|
|
* Returns the default event bus, which is a statically initialized singleton instance.
|
2020-09-06 15:32:54 +02:00
|
|
|
*
|
2021-10-15 08:25:38 +02:00
|
|
|
* @return the default event bus
|
2020-09-06 15:32:54 +02:00
|
|
|
* @since 0.0.2
|
|
|
|
*/
|
|
|
|
public static EventBus getInstance() {
|
2021-10-15 08:25:38 +02:00
|
|
|
return singletonInstance;
|
2020-09-06 15:32:54 +02:00
|
|
|
}
|
|
|
|
|
2021-11-01 09:42:12 +01:00
|
|
|
/**
|
|
|
|
* Event handler bindings (target class to handlers registered for that class), does not contain
|
|
|
|
* other (polymorphic) handlers.
|
|
|
|
*
|
|
|
|
* @since 0.0.1
|
|
|
|
*/
|
|
|
|
private final Map<Class<?>, TreeSet<EventHandler>> bindings = new ConcurrentHashMap<>();
|
|
|
|
|
|
|
|
/**
|
2021-11-01 20:52:14 +01:00
|
|
|
* Stores all registered event listeners (which declare event handlers) and prevents them from
|
|
|
|
* being garbage collected.
|
2021-11-01 09:42:12 +01:00
|
|
|
*
|
|
|
|
* @since 0.0.1
|
|
|
|
*/
|
|
|
|
private final Set<Object> registeredListeners = ConcurrentHashMap.newKeySet();
|
|
|
|
|
|
|
|
/**
|
2021-11-01 20:52:14 +01:00
|
|
|
* The current event dispatching state, local to each thread.
|
2021-11-01 09:42:12 +01:00
|
|
|
*
|
|
|
|
* @since 0.1.0
|
|
|
|
*/
|
|
|
|
private final ThreadLocal<DispatchState> dispatchState =
|
2021-02-15 09:25:16 +01:00
|
|
|
ThreadLocal.withInitial(DispatchState::new);
|
2020-09-02 16:15:31 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Dispatches an event to all event handlers registered for it in descending order of their
|
|
|
|
* priority.
|
|
|
|
*
|
|
|
|
* @param event the event to dispatch
|
2021-11-01 09:42:12 +01:00
|
|
|
* @throws EventBusException if an event handler isn't accessible or has an invalid signature
|
|
|
|
* @throws NullPointerException if the specified event is {@code null}
|
2020-09-02 16:15:31 +02:00
|
|
|
* @since 0.0.1
|
|
|
|
*/
|
2021-03-15 08:29:15 +01:00
|
|
|
public void dispatch(Object event) throws EventBusException {
|
2020-09-08 19:47:21 +02:00
|
|
|
Objects.requireNonNull(event);
|
2020-09-20 15:27:51 +02:00
|
|
|
logger.log(Level.INFO, "Dispatching event {0}", event);
|
2020-10-02 17:50:11 +02:00
|
|
|
|
2021-07-12 10:19:04 +02:00
|
|
|
// Look up dispatch state
|
2020-10-02 17:50:11 +02:00
|
|
|
var state = dispatchState.get();
|
2021-07-12 10:19:04 +02:00
|
|
|
|
|
|
|
// Increment nesting count (becomes > 1 during nested dispatches)
|
|
|
|
++state.nestingCount;
|
2020-10-02 17:50:11 +02:00
|
|
|
|
2021-11-01 21:36:24 +01:00
|
|
|
Iterator<EventHandler> handlers = getHandlersFor(event.getClass()).iterator();
|
2021-02-19 16:04:49 +01:00
|
|
|
if (handlers.hasNext()) {
|
|
|
|
while (handlers.hasNext())
|
|
|
|
if (state.isCancelled) {
|
|
|
|
logger.log(Level.INFO, "Cancelled dispatching event {0}", event);
|
|
|
|
state.isCancelled = false;
|
|
|
|
break;
|
|
|
|
} else {
|
2021-02-21 10:36:06 +01:00
|
|
|
try {
|
|
|
|
handlers.next().execute(event);
|
|
|
|
} catch (InvocationTargetException e) {
|
2021-07-07 22:06:07 +02:00
|
|
|
if (e.getCause() instanceof Error)
|
2021-03-15 08:29:15 +01:00
|
|
|
|
|
|
|
// Transparently pass error to the caller
|
|
|
|
throw (Error) e.getCause();
|
2021-07-07 22:06:07 +02:00
|
|
|
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);
|
2021-02-21 10:36:06 +01:00
|
|
|
else
|
|
|
|
|
|
|
|
// Dispatch exception event
|
|
|
|
dispatch(new ExceptionEvent(this, event, e.getCause()));
|
|
|
|
}
|
2021-02-19 16:04:49 +01:00
|
|
|
}
|
2021-02-21 10:36:06 +01:00
|
|
|
} else if (event instanceof DeadEvent || event instanceof ExceptionEvent) {
|
|
|
|
|
2021-02-20 21:40:48 +01:00
|
|
|
// Warn about the dead event not being handled
|
|
|
|
logger.log(Level.WARNING, "{0} not handled", event);
|
2021-02-21 10:36:06 +01:00
|
|
|
} else {
|
|
|
|
|
|
|
|
// Dispatch dead event
|
|
|
|
dispatch(new DeadEvent(this, event));
|
2021-02-19 16:04:49 +01:00
|
|
|
}
|
2020-10-02 17:50:11 +02:00
|
|
|
|
2021-07-12 10:19:04 +02:00
|
|
|
// Decrement nesting count (becomes 0 when all dispatches on the thread are finished)
|
|
|
|
--state.nestingCount;
|
2020-10-02 17:50:11 +02:00
|
|
|
|
|
|
|
logger.log(Level.DEBUG, "Finished dispatching event {0}", event);
|
2020-09-02 16:15:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-02-19 16:04:49 +01:00
|
|
|
* Searches for the event handlers bound to an event class. This includes polymorphic handlers
|
|
|
|
* that are bound to a supertype of the event class.
|
2020-09-02 16:15:31 +02:00
|
|
|
*
|
2021-11-02 19:33:18 +01:00
|
|
|
* @param eventType the event type to use for the search
|
2021-11-01 21:36:24 +01:00
|
|
|
* @return a navigable set containing the applicable handlers in descending order of priority
|
|
|
|
* @since 1.2.0
|
2020-09-02 16:15:31 +02:00
|
|
|
*/
|
2021-11-02 19:33:18 +01:00
|
|
|
private NavigableSet<EventHandler> getHandlersFor(Class<?> eventType) {
|
2020-09-20 12:20:29 +02:00
|
|
|
|
|
|
|
// Get handlers defined for the event class
|
2021-11-02 19:33:18 +01:00
|
|
|
TreeSet<EventHandler> handlers =
|
|
|
|
bindings.getOrDefault(eventType, new TreeSet<>(byPriority));
|
2020-09-20 12:20:29 +02:00
|
|
|
|
2021-02-19 16:04:49 +01:00
|
|
|
// Get polymorphic handlers
|
2020-09-20 12:20:29 +02:00
|
|
|
for (var binding : bindings.entrySet())
|
2021-11-02 19:33:18 +01:00
|
|
|
if (binding.getKey().isAssignableFrom(eventType))
|
2020-09-20 12:20:29 +02:00
|
|
|
for (var handler : binding.getValue())
|
2021-02-15 10:55:30 +01:00
|
|
|
if (handler.isPolymorphic())
|
2020-09-20 12:20:29 +02:00
|
|
|
handlers.add(handler);
|
|
|
|
|
2021-11-01 21:36:24 +01:00
|
|
|
return handlers;
|
2020-09-02 16:15:31 +02:00
|
|
|
}
|
|
|
|
|
2020-10-02 17:50:11 +02:00
|
|
|
/**
|
|
|
|
* Cancels an event that is currently dispatched from inside an event handler.
|
|
|
|
*
|
|
|
|
* @throws EventBusException if the calling thread is not an active dispatching thread
|
|
|
|
* @since 0.1.0
|
|
|
|
*/
|
|
|
|
public void cancel() {
|
|
|
|
var state = dispatchState.get();
|
2021-07-12 10:19:04 +02:00
|
|
|
if (state.nestingCount > 0 && !state.isCancelled)
|
2020-10-02 17:50:11 +02:00
|
|
|
state.isCancelled = true;
|
|
|
|
else
|
|
|
|
throw new EventBusException("Calling thread not an active dispatching thread!");
|
|
|
|
}
|
|
|
|
|
2020-09-02 16:15:31 +02:00
|
|
|
/**
|
|
|
|
* Registers an event listener at this event bus.
|
|
|
|
*
|
|
|
|
* @param listener the listener to register
|
2021-11-01 09:42:12 +01:00
|
|
|
* @throws EventBusException if the listener is already registered or a declared event
|
|
|
|
* handler does not comply with the specification
|
|
|
|
* @throws NullPointerException if the specified listener is {@code null}
|
2020-09-02 16:15:31 +02:00
|
|
|
* @since 0.0.1
|
|
|
|
* @see Event
|
|
|
|
*/
|
2021-02-15 14:43:34 +01:00
|
|
|
public void registerListener(Object listener) throws EventBusException {
|
2020-09-08 19:47:21 +02:00
|
|
|
Objects.requireNonNull(listener);
|
2020-09-02 16:15:31 +02:00
|
|
|
if (registeredListeners.contains(listener))
|
|
|
|
throw new EventBusException(listener + " already registered!");
|
2020-09-20 15:27:51 +02:00
|
|
|
logger.log(Level.INFO, "Registering event listener {0}", listener.getClass().getName());
|
|
|
|
boolean handlerBound = false;
|
2020-09-02 16:15:31 +02:00
|
|
|
|
2021-03-14 11:18:11 +01:00
|
|
|
// Predefined handler polymorphism
|
|
|
|
boolean polymorphic = false;
|
|
|
|
if (listener.getClass().isAnnotationPresent(Polymorphic.class))
|
|
|
|
polymorphic = listener.getClass().getAnnotation(Polymorphic.class).value();
|
|
|
|
|
|
|
|
// Predefined handler priority
|
2021-03-16 10:17:24 +01:00
|
|
|
int priority = DEFAULT_PRIORITY;
|
2021-03-14 11:18:11 +01:00
|
|
|
if (listener.getClass().isAnnotationPresent(Priority.class))
|
|
|
|
priority = listener.getClass().getAnnotation(Priority.class).value();
|
|
|
|
|
2020-09-02 16:15:31 +02:00
|
|
|
registeredListeners.add(listener);
|
|
|
|
for (var method : listener.getClass().getDeclaredMethods()) {
|
|
|
|
Event annotation = method.getAnnotation(Event.class);
|
|
|
|
|
|
|
|
// Skip methods without annotations
|
|
|
|
if (annotation == null)
|
|
|
|
continue;
|
|
|
|
|
2020-09-08 19:47:21 +02:00
|
|
|
// Initialize and bind the handler
|
2021-11-02 19:33:18 +01:00
|
|
|
bindHandler(
|
|
|
|
new ReflectiveEventHandler(listener, method, annotation, polymorphic, priority));
|
2020-09-20 15:27:51 +02:00
|
|
|
handlerBound = true;
|
2020-09-02 16:15:31 +02:00
|
|
|
}
|
2020-09-20 15:27:51 +02:00
|
|
|
|
2021-02-15 09:25:16 +01:00
|
|
|
if (!handlerBound)
|
2020-09-20 15:27:51 +02:00
|
|
|
logger.log(
|
|
|
|
Level.WARNING,
|
|
|
|
"No event handlers bound for event listener {0}",
|
2021-02-15 09:25:16 +01:00
|
|
|
listener.getClass().getName());
|
2020-09-02 16:15:31 +02:00
|
|
|
}
|
|
|
|
|
2021-11-02 19:33:18 +01:00
|
|
|
/**
|
|
|
|
* 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}.
|
|
|
|
*
|
2021-11-04 15:54:36 +01:00
|
|
|
* @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
|
2021-11-02 19:33:18 +01:00
|
|
|
* @since 1.2.0
|
|
|
|
* @see #registerListener(Class, Consumer, boolean, int)
|
|
|
|
*/
|
2021-11-04 15:54:36 +01:00
|
|
|
public <E> void registerListener(Class<E> eventType, Consumer<E> eventListener) {
|
|
|
|
registerListener(eventType, eventListener, false, DEFAULT_PRIORITY);
|
2021-11-02 19:33:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Registers a callback listener, which is a consumer that is invoked when an event occurs. The
|
|
|
|
* listener has the {@link #DEFAULT_PRIORITY}.
|
|
|
|
*
|
2021-11-04 15:54:36 +01:00
|
|
|
* @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
|
2021-11-02 19:33:18 +01:00
|
|
|
* @since 1.2.0
|
|
|
|
* @see #registerListener(Class, Consumer, boolean, int)
|
|
|
|
*/
|
2021-11-04 15:54:36 +01:00
|
|
|
public <E> void registerListener(Class<E> eventType, Consumer<E> eventListener,
|
2021-11-02 19:33:18 +01:00
|
|
|
boolean polymorphic) {
|
2021-11-04 15:54:36 +01:00
|
|
|
registerListener(eventType, eventListener, polymorphic, DEFAULT_PRIORITY);
|
2021-11-02 19:33:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Registers a callback listener, which is a consumer that is invoked when an event occurs. The
|
|
|
|
* listener is not polymorphic.
|
|
|
|
*
|
2021-11-04 15:54:36 +01:00
|
|
|
* @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
|
2021-11-02 19:33:18 +01:00
|
|
|
* @since 1.2.0
|
|
|
|
* @see #registerListener(Class, Consumer, boolean, int)
|
|
|
|
*/
|
2021-11-04 15:54:36 +01:00
|
|
|
public <E> void registerListener(Class<E> eventType, Consumer<E> eventListener, int priority) {
|
|
|
|
registerListener(eventType, eventListener, false, priority);
|
2021-11-02 19:33:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Registers a callback listener, which is a consumer that is invoked when an event occurs.
|
|
|
|
*
|
2021-11-04 15:54:36 +01:00
|
|
|
* @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
|
2021-11-02 19:33:18 +01:00
|
|
|
* @since 1.2.0
|
|
|
|
*/
|
2021-11-04 15:54:36 +01:00
|
|
|
public <E> void registerListener(Class<E> eventType, Consumer<E> eventListener,
|
|
|
|
boolean polymorphic,
|
2021-11-02 19:33:18 +01:00
|
|
|
int priority) {
|
2021-11-04 15:54:36 +01:00
|
|
|
Objects.requireNonNull(eventListener);
|
|
|
|
if (registeredListeners.contains(eventListener))
|
|
|
|
throw new EventBusException(eventListener + " already registered!");
|
2021-11-02 19:33:18 +01:00
|
|
|
logger.log(Level.INFO, "Registering callback event listener {0}",
|
2021-11-04 15:54:36 +01:00
|
|
|
eventListener.getClass().getName());
|
2021-11-02 19:33:18 +01:00
|
|
|
|
2021-11-04 15:54:36 +01:00
|
|
|
registeredListeners.add(eventListener);
|
|
|
|
bindHandler(new CallbackEventHandler(eventType, eventListener, polymorphic, priority));
|
2021-11-02 19:33:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2020-09-02 16:15:31 +02:00
|
|
|
/**
|
|
|
|
* Removes a specific listener from this event bus.
|
|
|
|
*
|
|
|
|
* @param listener the listener to remove
|
|
|
|
* @since 0.0.1
|
|
|
|
*/
|
2021-02-15 14:43:34 +01:00
|
|
|
public void removeListener(Object listener) {
|
2020-09-08 19:47:21 +02:00
|
|
|
Objects.requireNonNull(listener);
|
2020-09-20 15:27:51 +02:00
|
|
|
logger.log(Level.INFO, "Removing event listener {0}", listener.getClass().getName());
|
|
|
|
|
2021-11-01 09:42:12 +01:00
|
|
|
// Remove bindings from binding map
|
2020-09-02 16:15:31 +02:00
|
|
|
for (var binding : bindings.values()) {
|
|
|
|
var it = binding.iterator();
|
2020-09-20 15:27:51 +02:00
|
|
|
while (it.hasNext()) {
|
|
|
|
var handler = it.next();
|
|
|
|
if (handler.getListener() == listener) {
|
|
|
|
logger.log(Level.DEBUG, "Unbinding event handler {0}", handler);
|
2020-09-02 16:15:31 +02:00
|
|
|
it.remove();
|
2020-09-20 15:27:51 +02:00
|
|
|
}
|
|
|
|
}
|
2020-09-02 16:15:31 +02:00
|
|
|
}
|
2021-11-01 09:42:12 +01:00
|
|
|
|
|
|
|
// Remove the listener itself
|
2020-09-02 16:15:31 +02:00
|
|
|
registeredListeners.remove(listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes all event listeners from this event bus.
|
|
|
|
*
|
|
|
|
* @since 0.0.1
|
|
|
|
*/
|
|
|
|
public void clearListeners() {
|
2020-09-20 15:27:51 +02:00
|
|
|
logger.log(Level.INFO, "Clearing event listeners");
|
2020-09-02 16:15:31 +02:00
|
|
|
bindings.clear();
|
|
|
|
registeredListeners.clear();
|
|
|
|
}
|
|
|
|
|
2021-11-01 21:36:24 +01:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2021-11-25 14:34:13 +01:00
|
|
|
public String debugExecutionOrder(Class<?> eventType) {
|
2021-11-01 21:36:24 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2020-09-02 16:15:31 +02:00
|
|
|
/**
|
|
|
|
* Provides an unmodifiable view of the event listeners registered at this event bus.
|
|
|
|
*
|
|
|
|
* @return all registered event listeners
|
|
|
|
* @since 0.0.1
|
|
|
|
*/
|
2021-02-15 14:43:34 +01:00
|
|
|
public Set<Object> getRegisteredListeners() {
|
2020-09-02 16:15:31 +02:00
|
|
|
return Collections.unmodifiableSet(registeredListeners);
|
|
|
|
}
|
|
|
|
}
|