2020-09-03 10:05:00 +02:00
# Event Bus
2020-09-02 11:25:13 +02:00
2020-09-06 14:57:17 +02:00
## Introduction
2020-09-03 10:05:00 +02:00
This library allows passing events between different objects without them having a direct reference to each other.
2021-02-15 14:43:34 +01:00
Any object can serve as an event.
2020-09-03 10:05:00 +02:00
2021-02-17 08:56:42 +01:00
Using an instance of the `EventBus` class, an instance of the event class can be dispatched.
2020-09-03 10:05:00 +02:00
This means that it will be forwarded to all listeners registered for it at the event bus.
2020-09-08 09:38:46 +02:00
In addition, a singleton instance of the event bus is provided by the `EventBus#getInstance()` method.
2020-09-03 10:05:00 +02:00
To listen to events, register event handling methods using the `Event` annotation.
2021-02-17 08:56:42 +01:00
For this to work, the method must declare a single parameter of the desired event type.
2020-09-08 19:47:21 +02:00
Alternatively, a parameter-less event handler can be declared as shown [below ](#parameter-less-event-handlers ).
2020-09-03 10:05:00 +02:00
2020-09-06 14:57:17 +02:00
## A Simple Example
2021-02-15 14:43:34 +01:00
Lets look at a simple example: we declare the empty class `SimpleEvent` whose objects can be used as events.
2020-09-03 10:05:00 +02:00
```java
2021-02-15 14:43:34 +01:00
public class SimpleEvent {}
2020-09-03 10:05:00 +02:00
```
Next, an event listener for the `SimpleEvent` is declared:
```java
2021-02-14 21:48:24 +01:00
import dev.kske.eventbus.core.*;
2020-09-03 10:05:00 +02:00
2021-02-15 14:43:34 +01:00
public class SimpleEventListener {
2020-09-03 10:05:00 +02:00
public SimpleEventListener() {
// Register this listener at the event bus
2020-09-08 09:38:46 +02:00
EventBus.getInstance().register(this);
2020-09-03 10:05:00 +02:00
// Dispatch a SimpleEvent
2020-09-08 09:38:46 +02:00
EventBus.getInstance().dispatch(new SimpleEvent());
2020-09-03 10:05:00 +02:00
}
@Event
private void onSimpleEvent(SimpleEvent event) {
System.out.println("SimpleEvent received!");
}
}
```
2020-09-26 09:59:08 +02:00
Note that creating static event handlers like this
```java
2021-01-08 09:45:07 +01:00
@Event
2021-02-15 10:55:30 +01:00
private static void onSimpleEvent(SimpleEvent event) { ... }
2020-09-26 09:59:08 +02:00
```
is technically possible, however you would still have to create an instance of the event listener to register it at an event bus.
2021-02-15 10:55:30 +01:00
## Polymorphic Event Handlers
2020-09-20 12:20:29 +02:00
2021-02-14 21:48:24 +01:00
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.
2021-02-15 10:55:30 +01:00
To include subtypes for an event handler, use the `@Polymorphic` annotation in addition to `@Event` :
2020-09-20 12:20:29 +02:00
```java
2021-02-15 10:55:30 +01:00
@Event
@Polymorphic
private void onSimpleEvent(SimpleEvent event) { ... }
2020-09-20 12:20:29 +02:00
```
2021-02-15 10:55:30 +01:00
## Event Handler Execution Order
2020-09-20 14:35:50 +02:00
2021-02-15 12:06:33 +01:00
Sometimes when using multiple handlers for one event, it might be useful to define in which order they will be executed.
Event Bus assigns a priority to every handler, which is `100` by default, but can be explicitly set using the `@Priority` annotation in addition to `@Event` :
2020-09-20 14:35:50 +02:00
```java
2021-02-15 12:06:33 +01:00
@Event
@Priority (250)
private void onSimpleEvent(SimpleEvent event) { ... }
2020-09-20 14:35:50 +02:00
```
**Important:**
2021-02-15 12:06:33 +01:00
Events are dispatched to handlers in descending order of their priority.
The execution order is undefined for handlers with the same priority.
2020-09-20 14:35:50 +02:00
2021-02-15 10:55:30 +01:00
## Parameter-Less Event Handlers
2020-09-08 19:47:21 +02:00
In some cases an event handler is not interested in the dispatched event instance.
To avoid declaring a useless parameter just to specify the event type of the handler, there is an alternative:
```java
2021-02-15 13:42:20 +01:00
@Event (SimpleEvent.class)
2020-09-08 19:47:21 +02:00
private void onSimpleEvent() {
System.out.println("SimpleEvent received!");
}
```
2021-02-15 13:42:20 +01:00
Make sure that you **do not** both declare a parameter and specify the event type in the annotation, as this would be ambiguous.
2020-09-06 14:57:17 +02:00
2021-11-24 10:45:58 +01:00
## 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.
2021-03-14 11:44:56 +01:00
## 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.
Instead of defining that priority for each handler, it can be defined at the listener level by annotating the listener itself.
The same applies to polymorphism.
2021-02-15 10:55:30 +01:00
## Event Consumption
2020-10-11 11:25:18 +02:00
2021-01-03 17:00:20 +01:00
In some cases it might be useful to stop the propagation of an event.
Event Bus makes this possible with event consumption:
2020-10-11 11:25:18 +02:00
```java
2021-02-15 13:42:20 +01:00
@Event (SimpleEvent.class)
2021-02-15 12:06:33 +01:00
@Priority (100)
2020-10-11 11:25:18 +02:00
private void onSimpleEvent() {
EventBus.getInstance().cancel();
}
2021-02-15 13:42:20 +01:00
@Event (SimpleEvent.class)
2021-02-15 12:06:33 +01:00
@Priority (50)
2020-10-11 11:25:18 +02:00
private void onSimpleEvent2() {
System.out.println("Will not be printed!");
}
```
2021-01-03 17:00:20 +01:00
In this example, the second method will not be executed as it has a lower priority and the event will not be propagated after consumption.
This applies to all event handlers that would have been executed after the one consuming the event.
2020-10-11 11:25:18 +02:00
**Important:**
2021-01-03 17:00:20 +01:00
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.
2020-10-11 11:25:18 +02:00
2021-02-21 13:50:12 +01:00
## 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.
2021-11-24 10:37:21 +01:00
## Debugging
In more complex setups, taking a look at the event handler execution order can be helpful for debugging.
Event Bus offers a method for this purpose which can be used as follows:
```java
System.out.println(EventBus.getInstance().printExecutionOrder(SimpleEvent.class));
```
Then, the execution order can be inspected in the console.
2020-09-06 14:57:17 +02:00
## Installation
2021-02-17 08:56:42 +01:00
Event Bus is available in Maven Central.
To include it inside your project, just add the following dependency to your `pom.xml` :
2020-09-06 14:57:17 +02:00
```xml
< dependencies >
< dependency >
< groupId > dev.kske< / groupId >
2021-02-17 08:56:42 +01:00
< artifactId > event-bus-core< / artifactId >
2021-03-28 10:36:37 +02:00
< version > 1.1.0< / version >
2020-09-06 14:57:17 +02:00
< / dependency >
< / dependencies >
```
2021-02-14 21:48:24 +01:00
Then, require the Event Bus Core module in your `module-info.java` :
```java
requires dev.kske.eventbus.core;
```
2021-02-15 13:42:20 +01:00
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:
```java
opens my.module to dev.kske.eventbus.core;
```
2021-02-17 08:22:48 +01:00
## Compile-Time Error Checking with Event Bus Proc
2021-02-14 21:48:24 +01:00
2021-02-17 08:22:48 +01:00
To assist you with writing event listeners, the Event Bus Proc (Annotation Processor) module enforces correct usage of the `@Event` annotation during compile time.
2021-02-14 21:48:24 +01:00
This reduces difficult-to-debug bugs that occur during runtime to compile-time errors which can be easily fixed.
2021-02-15 14:43:34 +01:00
The event annotation processor detects invalid event handlers and event type issues with more to come in future versions.
2021-02-14 21:48:24 +01:00
When using Maven, it can be registered using the Maven Compiler Plugin:
```xml
< plugin >
< groupId > org.apache.maven.plugins< / groupId >
< artifactId > maven-compiler-plugin< / artifactId >
< version > 3.8.1< / version >
< configuration >
< annotationProcessorPaths >
< annotationProcessorPath >
< groupId > dev.kske< / groupId >
2021-02-17 08:56:42 +01:00
< artifactId > event-bus-proc< / artifactId >
2021-03-28 10:36:37 +02:00
< version > 1.1.0< / version >
2021-02-14 21:48:24 +01:00
< / annotationProcessorPath >
< / annotationProcessorPaths >
< / configuration >
< / plugin >
```
2021-02-17 08:56:42 +01:00
Alternatively, a JAR file containing the processor is offered with each release for the use within IDEs and environments without Maven support.