S - The type of the ListenerStore this EventProvider uses.public interface EventProvider<S extends ListenerStore>
EventProvider instances are used to inform clients about certain events. They
implement the logic of how Listeners are notified about an
Event. The way in which they are notified is an internal property of
the actual EventProvider instance. For example, one kind of EventProvider
might create a new thread for notifying the registered listeners or it may
simply notify them using the current thread.
jeve provides a fluent builder API to configure and create EventProvider
instances. See configure() for more information on how to build
providers. A simple provider which dispatches Events from the current thread
can be created by calling:
EventProvider<?> eventProvider = EventProvider.configure()
.defaultStore()
.useSynchronousProvider()
.create();
As of jeve 2.0.0, all shipped provider implementations are public and thus also constructible by simply calling their constructors.
Listeners are managed by a ListenerStore. An instance of such a store
is typically supplied to the EventProvider at construction time. It is
allowed that multiple providers share a single ListenerStore. Listeners are
registered and unregistered by calling the respective methods on the
ListenerStore which belongs to this provider. The store can be obtained by
calling listeners().
eventProvider.listeners().add(UserListener.class, myUserListener);
eventProvider.listeners().remove(UserListener.class, myUserListener);
The same listener object can be registered for distinct listener classes if
it implements different listeners. The Listener interface has two
default methods which are called when a listener is registered or removed
respectively. Client code should never query the registered listeners
directly from a ListenerStore. The reason is, that EventProviders use
internal iteration when notifying listeners. This reduces the use
cases where client code explicitly needs a list of listeners. The logic of
how listeners are iterated is moved into the framework, reducing duplicated
and error prone code on the client side.
To notify the registered listeners, you need to specify the Event
instance which is passed to each listener and the actual method to call on
each listener as a method reference. Jeve obtains the class of the listeners
to notify from the event's getListenerClass
method. Thus, the class has not to be specified explicitly for each dispatch
action. Here is an example of notifying listeners which have been registered
for the class UserListener.
// create an event which holds its source and some additional data UserEvent e = new UserEvent(this, user); // notify all UserListeners with this event. eventProvider.dispatch(e, UserListener::userAdded);
On each listener which is registered for the class UserListener, the
method userAdded is called and gets passed the event instance
e. Dispatch is the core of any
EventProvider. It implements the logic of how the listeners are notified in a
way that is transparent for the user of the EventProvider.
If a listener has only one listening method, it is obsolete to specify the
method reference for every dispatch action. For this case, jeve provides the
DefaultDispatchable and an overload of
dispatch(DefaultDispatchable) which allows to dispatch an event
without specifying the method reference again.
The main goal of jeve is, that event delegation must never be interrupted
unintentionally. When handling events, you don't want the dispatching process
to stop if one of the listeners throws an unchecked exception. Therefore,
jeve uses ExceptionCallbacks to notify client code
about any exception. After notifying the callback, event delegation continues
with the next listener. AbortionException can be thrown from within
the callback method to explicitly stop event delegation with an exception.
All other exceptions thrown by the callback will be swallowed.
A default ExceptionCallback can be set by using
setExceptionCallback(ExceptionCallback). Additionally, you can set a
callback for a single dispatch action by using an override of
dispatch. If you do
not specify a callback, a default instance will be used.
An EventProvider is said to be sequential, if it guarantees that
listeners are notified in the order in which they were registered for a
certain listener class. EventProviders report this property with
isSequential(). Whether an EventProvider actually is sequential
depends on its implementation of the dispatch method and the currently used
ListenerStore. For example, a provider which notifies each listener
within a separate thread is not sequential. Likewise, a provider which
notifies listeners sequentially within one thread, but uses a ListenerStore
which re-orders listeners (like PriorityListenerStore), is not
sequential.
As stated above, event delegation can generally not be interrupted by
throwing exceptions (as they are passed to the ExceptionCallack). Instead,
listeners can modify the passed Event instance and set its
handled property to true.
Before notifying the next listener, the EventProvider queries the
isHandled property of the currently processed
event. If it is handled, event delegation stops and no further listeners are
notified.
The behavior of aborting event delegation on non-sequential EventProviders is undefined.
| Modifier and Type | Method and Description |
|---|---|
boolean |
canDispatch()
Gets whether this EventProvider is ready for dispatching.
|
void |
close()
Closes this EventProvider and its
ListenerStore (thus, removes
all registered Listeners from the store). |
static EventProviderConfigurator |
configure()
Provides a fluent builder API to construct several kinds of
EventProviders.
|
static EventProvider<DefaultListenerStore> |
createDefault()
Convenience method for creating a synchronous event provider which uses a
default listener store.
|
<L extends Listener,E extends DefaultDispatchable> |
dispatch(DefaultDispatchable event)
Dispatches the given event by calling its
DefaultDispatchable.defaultDispatch(EventProvider, ExceptionCallback)
method, passing this as first argument and the currently set
ExceptionCallback as second argument. |
default void |
dispatch(DefaultDispatchable event,
ExceptionCallback ec)
Dispatches the given event by calling its
DefaultDispatchable.defaultDispatch(EventProvider, ExceptionCallback)
method, passing this as first argument and the given
ExceptionCallback as second argument. |
default <L extends Listener,E extends DefaultTargetEvent<?,E,L>> |
dispatch(E event)
Deprecated.
Since 3.0.0 - Use
dispatch(DefaultDispatchable)
instead. |
<L extends Listener,E extends Event<?,L>> |
dispatch(E event,
BiConsumer<L,E> bc)
Notifies all listeners of a certain kind about an occurred event.
|
<L extends Listener,E extends Event<?,L>> |
dispatch(E event,
BiConsumer<L,E> bc,
ExceptionCallback ec)
Notifies all listeners of a certain kind about an occurred event with
explicit error handling.
|
default <L extends Listener,E extends DefaultTargetEvent<?,E,L>> |
dispatch(E event,
ExceptionCallback ec)
Deprecated.
Since 3.0.0 - Use
dispatch(DefaultDispatchable, ExceptionCallback)
instead. |
boolean |
isSequential()
Returns whether this EventProvider is sequential, which means it strictly
notifies listeners in the order in which they were registered for a
certain event.
|
S |
listeners()
Retrieves the
ListenerStore which supplies Listeners to this EventProvider. |
void |
setExceptionCallback(ExceptionCallback ec)
Sets the default
ExceptionCallback which will be notified about
exceptions when dispatching events without explicitly specifying an
ExceptionCallback. |
static EventProviderConfigurator configure()
Configuring an EventProvider always starts with choosing an appropriate
ListenerStore. In most cases, the default store is sufficient,
but you could also use a store which provides listener prioritization
like in:
EventProvider<PriorityListenerStore> eventProvider = EventProvider
.configure()
.store(PriorityListenerStore::new)
.useSynchronousEventProvider()
.create();
After configuring the ListenerStore, several other attributes can be set.
E.g. the ExceptionCallback to use or, on some threaded
EventProviders, the ExecutorService to use. When configuring multiple
attributes, methods can be chained using and() as shown in
the example below. After your configuration is final, you can either
directly obtain an EventProvider instance using create() or
a Supplier using asSupplier() which can be used to
recreate the configuration at any time.
Supplier<EventProvider<?>> eventProvider = EventProvider
.configure()
.defaultStore()
.useSynchronousEventProvider().and()
.exceptionCallBack(myCallback)
.asSupplier();
static EventProvider<DefaultListenerStore> createDefault()
configure()S listeners()
ListenerStore which supplies Listeners to this EventProvider.<L extends Listener,E extends Event<?,L>> void dispatch(E event, BiConsumer<L,E> bc)
canDispatch(), this method returns immediately without doing
anything. This method will stop notifying further listeners if the passed
event has been marked 'handled' using Event.setHandled(boolean).
Consider an UserListener interface:
public interface UserListener extends Listener {
public void userAdded(UserEvent e);
public void userDeleted(UserEvent e);
}
Notifying all registered UserListeners about an added user is as easy as
calling
eventProvider.dispatchEvent(event, UserListener::userAdded)
This method uses the global ExceptionCallback provided to
setExceptionCallback(ExceptionCallback) or an default instance
if none has been explicitly set.
Note on concurrency: This method operates on a copy of the list of targeted listeners. This allows you to add/remove listeners from within a listening method.
Please note that neither parameter to this method must be null.
L - Type of the listeners which will be notified.E - Type of the event which will be passed to a listener.event - The occurred event which shall be passed to each listener.bc - Function to delegate the event to the specific callback method
of the listener.IllegalArgumentException - If any of the passed arguments is
null.AbortionException - If a listener threw an AbortionException.<L extends Listener,E extends Event<?,L>> void dispatch(E event, BiConsumer<L,E> bc, ExceptionCallback ec)
canDispatch(), this method returns immediately
without doing anything. This method will stop notifying further listeners
if the passed event has been marked 'handled' using
Event.setHandled(boolean).
Consider an UserListener interface:
public interface UserListener extends Listener {
public void userAdded(UserEvent e);
public void userDeleted(UserEvent e);
}
Notifying all registered UserListeners about an added user is as easy as
calling
final ExceptionCallback callBack = new EceptionCallback() { ... };
eventProvider.dispatchEvent(event, UserListener::userAdded, callBack);
The ExceptionCallback gets notified when any of the listeners
throws an unexpected exception. If the exception handler itself throws an
exception, it will be ignored. The callback provided to this method takes
precedence over the global callback provided by
setExceptionCallback(ExceptionCallback).
Note on concurrency: This method operates on a copy of the list of targeted listeners. This allows you to add/remove listeners from within a listening method.
Please note that neither parameter to this method must be null.
L - Type of the listeners which will be notified.E - Type of the event which will be passed to a listener.event - The occurred event which shall be passed to each listener.bc - Function to delegate the event to the specific callback method
of the listener.ec - Callback to be notified when any of the listeners throws an
exception.IllegalArgumentException - If any of the passed arguments is
null.AbortionException - If a listener threw an AbortionException.<L extends Listener,E extends DefaultDispatchable> void dispatch(DefaultDispatchable event)
DefaultDispatchable.defaultDispatch(EventProvider, ExceptionCallback)
method, passing this as first argument and the currently set
ExceptionCallback as second argument. The defaultDispatch method
will in return call
dispatch(Event, BiConsumer, ExceptionCallback) on this provider.L - Type of the listeners which will be notified.E - Type of the event which will be passed to a listener.event - The event to dispatch using its default dispatch method.IllegalArgumentException - If the passed event is nullAbortionException - If a listener threw an AbortionException.UnsupportedOperationException - If the given event does not support
default dispatching.DefaultDispatchabledefault void dispatch(DefaultDispatchable event, ExceptionCallback ec)
DefaultDispatchable.defaultDispatch(EventProvider, ExceptionCallback)
method, passing this as first argument and the given
ExceptionCallback as second argument. The defaultDispatch method
will in return call
dispatch(Event, BiConsumer, ExceptionCallback) on this provider.event - The event to dispatch using its default dispatch method.ec - The ExceptionCallback to use for this dispatch action.IllegalArgumentException - If any of the passed arguments is
null.AbortionException - If a listener threw an AbortionException.UnsupportedOperationException - If the given event does not support
default dispatching.DefaultDispatchable@Deprecated default <L extends Listener,E extends DefaultTargetEvent<?,E,L>> void dispatch(E event)
dispatch(DefaultDispatchable)
instead.DefaultTargetEvent with the logic of
dispatch(Event, BiConsumer).L - Type of the listeners which will be notified.E - Type of the event which will be passed to a listener.event - The occurred event which shall be passed to each listener.IllegalArgumentException - If the event is null.AbortionException - If a listener threw an AbortionException.@Deprecated default <L extends Listener,E extends DefaultTargetEvent<?,E,L>> void dispatch(E event, ExceptionCallback ec)
dispatch(DefaultDispatchable, ExceptionCallback)
instead.DefaultTargetEvent with the logic of
dispatch(Event, BiConsumer, ExceptionCallback).L - Type of the listeners which will be notified.E - Type of the event which will be passed to a listener.event - The occurred event which shall be passed to each listener.ec - Callback to be notified when any of the listeners throws an
exception.IllegalArgumentException - If any of the passed arguments is
null.AbortionException - If a listener threw an AbortionException.boolean canDispatch()
dispatchvoid setExceptionCallback(ExceptionCallback ec)
ExceptionCallback which will be notified about
exceptions when dispatching events without explicitly specifying an
ExceptionCallback. The ExceptionCallback which is installed by default
simply prints the stack traces to the error console.
You can reset the ExceptionCallback to the default handler by providing
null as parameter.
ec - The ExceptionCallback for handling event handler exceptions, or
null to use the default behavior.boolean isSequential()
Note: Implementors must obey the result of the
isSequential property of the
currently used ListenerStore. If the store is not sequential, this
provider won't be either.
void close()
ListenerStore (thus, removes
all registered Listeners from the store). Depending on the actual
implementation, the EventProvider might not be able to dispatch further
events after closing. On some implementations closing might have no
additional effect.Copyright © 2014–2015. All rights reserved.