cleanups to EventListenerGroup(Impl)

Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
Gavin King 2024-09-02 00:03:58 +02:00
parent 48fc2ee66d
commit 052eb0b78c
2 changed files with 130 additions and 123 deletions

View File

@ -7,7 +7,6 @@
package org.hibernate.event.service.internal; package org.hibernate.event.service.internal;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -32,6 +31,8 @@ import org.jboss.logging.Logger;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.singleton;
import static java.util.concurrent.CompletableFuture.completedFuture;
/** /**
* Standard EventListenerGroup implementation * Standard EventListenerGroup implementation
@ -42,8 +43,26 @@ import static java.util.Collections.emptyList;
class EventListenerGroupImpl<T> implements EventListenerGroup<T> { class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
private static final Logger log = Logger.getLogger( EventListenerGroupImpl.class ); private static final Logger log = Logger.getLogger( EventListenerGroupImpl.class );
private static final Set<DuplicationStrategy> DEFAULT_DUPLICATION_STRATEGIES = Collections.unmodifiableSet( makeDefaultDuplicationStrategy() );
private static final CompletableFuture COMPLETED = CompletableFuture.completedFuture( null ); private static final DuplicationStrategy DEFAULT_DUPLICATION_STRATEGY =
new DuplicationStrategy() {
@Override
public boolean areMatch(Object listener, Object original) {
return listener.getClass().equals( original.getClass() );
}
@Override
public Action getAction() {
return Action.ERROR;
}
};
private static final Set<DuplicationStrategy> DEFAULT_DUPLICATION_STRATEGIES =
singleton( DEFAULT_DUPLICATION_STRATEGY );
private static final CompletableFuture<?> COMPLETED = completedFuture( null );
@SuppressWarnings("unchecked")
private static <R> CompletableFuture<R> nullCompletion() {
return (CompletableFuture<R>) COMPLETED;
}
private final EventType<T> eventType; private final EventType<T> eventType;
private final CallbackRegistry callbackRegistry; private final CallbackRegistry callbackRegistry;
@ -56,10 +75,7 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
private volatile T[] listeners = null; private volatile T[] listeners = null;
private volatile List<T> listenersAsList = emptyList(); private volatile List<T> listenersAsList = emptyList();
public EventListenerGroupImpl( public EventListenerGroupImpl(EventType<T> eventType, CallbackRegistry callbackRegistry, boolean isJpaBootstrap) {
EventType<T> eventType,
CallbackRegistry callbackRegistry,
boolean isJpaBootstrap) {
this.eventType = eventType; this.eventType = eventType;
this.callbackRegistry = callbackRegistry; this.callbackRegistry = callbackRegistry;
this.isJpaBootstrap = isJpaBootstrap; this.isJpaBootstrap = isJpaBootstrap;
@ -83,7 +99,8 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
@Override @Override
public void clear() { public void clear() {
//Odd semantics: we're expected (for backwards compatibility) to also clear the default DuplicationStrategy. //Odd semantics: we're expected (for backwards compatibility)
// to also clear the default DuplicationStrategy.
duplicationStrategies = new LinkedHashSet<>(); duplicationStrategies = new LinkedHashSet<>();
setListeners( null ); setListeners( null );
} }
@ -92,13 +109,10 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
// ensure consistency between the two fields by delegating any mutation to both // ensure consistency between the two fields by delegating any mutation to both
// fields to this method. // fields to this method.
private synchronized void setListeners(T[] newListeners) { private synchronized void setListeners(T[] newListeners) {
this.listeners = newListeners; listeners = newListeners;
if ( newListeners == null || newListeners.length == 0 ) { listenersAsList = newListeners == null || newListeners.length == 0
this.listenersAsList = emptyList(); ? emptyList()
} : asList( newListeners );
else {
this.listenersAsList = asList( newListeners );
}
} }
@Override @Override
@ -107,7 +121,7 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
} }
@Override @Override
public final <U> void fireLazyEventOnEachListener(final Supplier<U> eventSupplier, final BiConsumer<T,U> actionOnEvent) { public final <U> void fireLazyEventOnEachListener(Supplier<U> eventSupplier, BiConsumer<T,U> actionOnEvent) {
final T[] ls = listeners; final T[] ls = listeners;
if ( ls != null && ls.length != 0 ) { if ( ls != null && ls.length != 0 ) {
final U event = eventSupplier.get(); final U event = eventSupplier.get();
@ -119,7 +133,7 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
} }
@Override @Override
public final <U> void fireEventOnEachListener(final U event, final BiConsumer<T,U> actionOnEvent) { public final <U> void fireEventOnEachListener(U event, BiConsumer<T,U> actionOnEvent) {
final T[] ls = listeners; final T[] ls = listeners;
if ( ls != null ) { if ( ls != null ) {
//noinspection ForLoopReplaceableByForEach //noinspection ForLoopReplaceableByForEach
@ -130,7 +144,7 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
} }
@Override @Override
public <U,X> void fireEventOnEachListener(final U event, final X parameter, final EventActionWithParameter<T, U, X> actionOnEvent) { public <U,X> void fireEventOnEachListener(U event, X parameter, EventActionWithParameter<T, U, X> actionOnEvent) {
final T[] ls = listeners; final T[] ls = listeners;
if ( ls != null ) { if ( ls != null ) {
//noinspection ForLoopReplaceableByForEach //noinspection ForLoopReplaceableByForEach
@ -144,9 +158,9 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
public <R, U, RL> CompletionStage<R> fireEventOnEachListener( public <R, U, RL> CompletionStage<R> fireEventOnEachListener(
final U event, final U event,
final Function<RL, Function<U, CompletionStage<R>>> fun) { final Function<RL, Function<U, CompletionStage<R>>> fun) {
CompletionStage<R> ret = COMPLETED; CompletionStage<R> ret = nullCompletion();
final T[] ls = listeners; final T[] ls = listeners;
if ( ls != null && ls.length != 0 ) { if ( ls != null ) {
for ( T listener : ls ) { for ( T listener : ls ) {
//to preserve atomicity of the Session methods //to preserve atomicity of the Session methods
//call apply() from within the arg of thenCompose() //call apply() from within the arg of thenCompose()
@ -159,9 +173,9 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
@Override @Override
public <R, U, RL, X> CompletionStage<R> fireEventOnEachListener( public <R, U, RL, X> CompletionStage<R> fireEventOnEachListener(
U event, X param, Function<RL, BiFunction<U, X, CompletionStage<R>>> fun) { U event, X param, Function<RL, BiFunction<U, X, CompletionStage<R>>> fun) {
CompletionStage<R> ret = COMPLETED; CompletionStage<R> ret = nullCompletion();
final T[] ls = listeners; final T[] ls = listeners;
if ( ls != null && ls.length != 0 ) { if ( ls != null ) {
for ( T listener : ls ) { for ( T listener : ls ) {
//to preserve atomicity of the Session methods //to preserve atomicity of the Session methods
//call apply() from within the arg of thenCompose() //call apply() from within the arg of thenCompose()
@ -173,9 +187,9 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
@Override @Override
public <R, U, RL> CompletionStage<R> fireLazyEventOnEachListener( public <R, U, RL> CompletionStage<R> fireLazyEventOnEachListener(
final Supplier<U> eventSupplier, Supplier<U> eventSupplier,
final Function<RL, Function<U, CompletionStage<R>>> fun) { Function<RL, Function<U, CompletionStage<R>>> fun) {
CompletionStage<R> ret = COMPLETED; CompletionStage<R> ret = nullCompletion();
final T[] ls = listeners; final T[] ls = listeners;
if ( ls != null && ls.length != 0 ) { if ( ls != null && ls.length != 0 ) {
final U event = eventSupplier.get(); final U event = eventSupplier.get();
@ -191,7 +205,8 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
@Override @Override
public void addDuplicationStrategy(DuplicationStrategy strategy) { public void addDuplicationStrategy(DuplicationStrategy strategy) {
if ( duplicationStrategies == DEFAULT_DUPLICATION_STRATEGIES ) { if ( duplicationStrategies == DEFAULT_DUPLICATION_STRATEGIES ) {
duplicationStrategies = makeDefaultDuplicationStrategy(); // At minimum make sure we do not register the same exact listener class multiple times.
duplicationStrategies = new LinkedHashSet<>( DEFAULT_DUPLICATION_STRATEGIES );
} }
duplicationStrategies.add( strategy ); duplicationStrategies.add( strategy );
} }
@ -212,19 +227,17 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
private void internalAppend(T listener) { private void internalAppend(T listener) {
prepareListener( listener ); prepareListener( listener );
final T[] listenersRead = this.listeners; final T[] listenersRead = listeners;
final T[] listenersWrite; final T[] listenersWrite;
if ( listenersRead == null ) { if ( listenersRead == null ) {
//noinspection unchecked listenersWrite = createListenerArrayForWrite( 1 );
listenersWrite = (T[]) Array.newInstance( eventType.baseListenerInterface(), 1 );
listenersWrite[0] = listener; listenersWrite[0] = listener;
} }
else { else {
final int size = listenersRead.length; final int size = listenersRead.length;
//noinspection unchecked listenersWrite = createListenerArrayForWrite( size + 1 );
listenersWrite = (T[]) Array.newInstance( eventType.baseListenerInterface(), size+1 );
// first copy the existing listeners // first copy the existing listeners
System.arraycopy( listenersRead, 0, listenersWrite, 0, size ); System.arraycopy( listenersRead, 0, listenersWrite, 0, size );
@ -251,19 +264,17 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
private void internalPrepend(T listener) { private void internalPrepend(T listener) {
prepareListener( listener ); prepareListener( listener );
final T[] listenersRead = this.listeners; final T[] listenersRead = listeners;
final T[] listenersWrite; final T[] listenersWrite;
if ( listenersRead == null ) { if ( listenersRead == null ) {
//noinspection unchecked listenersWrite = createListenerArrayForWrite( 1 );
listenersWrite = (T[]) Array.newInstance( eventType.baseListenerInterface(), 1 );
listenersWrite[0] = listener; listenersWrite[0] = listener;
} }
else { else {
final int size = listenersRead.length; final int size = listenersRead.length;
//noinspection unchecked listenersWrite = createListenerArrayForWrite( size + 1 );
listenersWrite = (T[]) Array.newInstance( eventType.baseListenerInterface(), size+1 );
// put the new one first // put the new one first
listenersWrite[0] = listener; listenersWrite[0] = listener;
@ -275,13 +286,15 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
} }
private void handleListenerAddition(T listener, Consumer<T> additionHandler) { private void handleListenerAddition(T listener, Consumer<T> additionHandler) {
final T[] listenersRead = this.listeners; final T[] listenersRead = listeners;
if ( listenersRead == null ) { if ( listenersRead == null ) {
additionHandler.accept( listener ); additionHandler.accept( listener );
return; return;
} }
final T[] listenersWrite = (T[]) Array.newInstance( eventType.baseListenerInterface(), listenersRead.length ); int size = listenersRead.length;
System.arraycopy( listenersRead, 0, listenersWrite, 0, listenersRead.length );
final T[] listenersWrite = createListenerArrayForWrite( size );
System.arraycopy( listenersRead, 0, listenersWrite, 0, size );
final boolean debugEnabled = log.isDebugEnabled(); final boolean debugEnabled = log.isDebugEnabled();
@ -292,40 +305,37 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
// strategy's action. Control it returned immediately after applying the action // strategy's action. Control it returned immediately after applying the action
// on match - meaning no further strategies are checked... // on match - meaning no further strategies are checked...
for ( int i = 0; i < listenersRead.length; i++ ) { for ( int i = 0; i < size; i++ ) {
final T existingListener = listenersRead[i]; final T existingListener = listenersRead[i];
if ( debugEnabled ) { if ( debugEnabled ) {
log.debugf( log.debugf( "Checking incoming listener [`%s`] for match against existing listener [`%s`]",
"Checking incoming listener [`%s`] for match against existing listener [`%s`]", listener, existingListener );
listener,
existingListener
);
} }
if ( strategy.areMatch( listener, existingListener ) ) { if ( strategy.areMatch( listener, existingListener ) ) {
if ( debugEnabled ) { if ( debugEnabled ) {
log.debugf( "Found listener match between `%s` and `%s`", listener, existingListener ); log.debugf( "Found listener match between `%s` and `%s`",
listener, existingListener );
} }
switch ( strategy.getAction() ) { final DuplicationStrategy.Action action = strategy.getAction();
case ERROR: { switch (action) {
case ERROR:
throw new EventListenerRegistrationException( "Duplicate event listener found" ); throw new EventListenerRegistrationException( "Duplicate event listener found" );
} case KEEP_ORIGINAL:
case KEEP_ORIGINAL: {
if ( debugEnabled ) { if ( debugEnabled ) {
log.debugf( "Skipping listener registration (%s) : `%s`", strategy.getAction(), listener ); log.debugf( "Skipping listener registration (%s) : `%s`",
action, listener );
} }
return; return;
} case REPLACE_ORIGINAL:
case REPLACE_ORIGINAL: {
if ( debugEnabled ) { if ( debugEnabled ) {
log.debugf( "Replacing listener registration (%s) : `%s` -> `%s`", strategy.getAction(), existingListener, listener ); log.debugf( "Replacing listener registration (%s) : `%s` -> `%s`",
action, existingListener, listener );
} }
prepareListener( listener ); prepareListener( listener );
listenersWrite[i] = listener; listenersWrite[i] = listener;
} }
}
// we've found a match - we should return: the match action has already been applied at this point // we've found a match - we should return: the match action has already been applied at this point
// apply all pending changes: // apply all pending changes:
@ -335,32 +345,35 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
} }
} }
// we did not find any match.. add it // we did not find any match, add it
checkAgainstBaseInterface( listener ); checkAgainstBaseInterface( listener );
performInjections( listener ); performInjections( listener );
additionHandler.accept( listener ); additionHandler.accept( listener );
} }
@SuppressWarnings("unchecked")
private T[] createListenerArrayForWrite(int len) {
return (T[]) Array.newInstance( eventType.baseListenerInterface(), len );
}
private void prepareListener(T listener) { private void prepareListener(T listener) {
checkAgainstBaseInterface( listener ); checkAgainstBaseInterface( listener );
performInjections( listener ); performInjections( listener );
} }
private void performInjections(T listener) { private void performInjections(T listener) {
if ( listener instanceof CallbackRegistryConsumer ) { if ( listener instanceof CallbackRegistryConsumer consumer ) {
( (CallbackRegistryConsumer) listener ).injectCallbackRegistry( callbackRegistry ); consumer.injectCallbackRegistry( callbackRegistry );
} }
if ( listener instanceof JpaBootstrapSensitive sensitive ) {
if ( listener instanceof JpaBootstrapSensitive ) { sensitive.wasJpaBootstrap( isJpaBootstrap );
( (JpaBootstrapSensitive) listener ).wasJpaBootstrap( isJpaBootstrap );
} }
} }
private void checkAgainstBaseInterface(T listener) { private void checkAgainstBaseInterface(T listener) {
if ( !eventType.baseListenerInterface().isInstance( listener ) ) { if ( !eventType.baseListenerInterface().isInstance( listener ) ) {
throw new EventListenerRegistrationException( throw new EventListenerRegistrationException( "Listener did not implement expected interface ["
"Listener did not implement expected interface [" + eventType.baseListenerInterface().getName() + "]" + eventType.baseListenerInterface().getName() + "]" );
);
} }
} }
@ -372,26 +385,6 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
@Override @Override
@Deprecated @Deprecated
public final Iterable<T> listeners() { public final Iterable<T> listeners() {
return this.listenersAsList; return listenersAsList;
}
private static Set<DuplicationStrategy> makeDefaultDuplicationStrategy() {
final Set<DuplicationStrategy> duplicationStrategies = new LinkedHashSet<>();
duplicationStrategies.add(
// At minimum make sure we do not register the same exact listener class multiple times.
new DuplicationStrategy() {
@Override
public boolean areMatch(Object listener, Object original) {
return listener.getClass().equals( original.getClass() );
}
@Override
public Action getAction() {
return Action.ERROR;
} }
} }
);
return duplicationStrategies;
}
}

View File

@ -49,9 +49,9 @@ public interface EventListenerGroup<T> {
/** /**
* Mechanism to more finely control the notion of duplicates. * Mechanism to more finely control the notion of duplicates.
* <p> * <p>
* For example, say you are registering listeners for an extension library. This extension library * For example, say you are registering listeners for an extension library. This
* could define a "marker interface" which indicates listeners related to it and register a strategy * extension library could define a "marker interface" which indicates listeners
* that checks against that marker interface. * related to it and register a strategy that checks against that marker interface.
* *
* @param strategy The duplication strategy * @param strategy The duplication strategy
*/ */
@ -64,10 +64,11 @@ public interface EventListenerGroup<T> {
void prependListeners(T... listeners); void prependListeners(T... listeners);
/** /**
* Clears both the list of event listeners and all DuplicationStrategy, * Clears both the list of event listeners and every {@link DuplicationStrategy},
* including the default duplication strategy. * including the default duplication strategy.
* @deprecated likely want to use {@link #clearListeners()} instead, which doesn't *
* also reset the registered DuplicationStrategy(ies). * @deprecated Use {@link #clearListeners()} instead, which doesn't also reset
* the registered {@link DuplicationStrategy}s.
*/ */
@Deprecated @Deprecated
void clear(); void clear();
@ -80,57 +81,65 @@ public interface EventListenerGroup<T> {
/** /**
* Fires an event on each registered event listener of this group. * Fires an event on each registered event listener of this group.
* *
* Implementation note (performance): * @implNote The first argument is a supplier so that events can avoid being created
* the first argument is a supplier so that events can avoid being created when no listener is registered. * when no listener is registered; The second argument is specifically
* the second argument is specifically designed to avoid needing a capturing lambda. * designed to avoid needing a capturing lambda.
* *
* @param <U> the kind of event * @param <U> the kind of event
*/ */
@Incubating @Incubating
<U> void fireLazyEventOnEachListener(final Supplier<U> eventSupplier, final BiConsumer<T,U> actionOnEvent); <U> void fireLazyEventOnEachListener(Supplier<U> eventSupplier, BiConsumer<T,U> actionOnEvent);
/** /**
* Similar as {@link #fireLazyEventOnEachListener(Supplier, BiConsumer)} except it doesn't use a {{@link Supplier}}: * Similar as {@link #fireLazyEventOnEachListener(Supplier, BiConsumer)} except it
* useful when there is no need to lazily initialize the event. * doesn't use a {{@link Supplier}}. Useful when there is no need to lazily initialize
* the event.
* *
* @param <U> the kind of event * @param <U> the kind of event
*/ */
@Incubating @Incubating
<U> void fireEventOnEachListener(final U event, final BiConsumer<T,U> actionOnEvent); <U> void fireEventOnEachListener(U event, BiConsumer<T,U> actionOnEvent);
/** /**
* Similar to {@link #fireEventOnEachListener(Object, BiConsumer)}, but allows passing a third parameter * Similar to {@link #fireEventOnEachListener(Object, BiConsumer)}, but allows passing
* to the consumer; our code based occasionally needs a third parameter: having this additional variant * a third parameter to the consumer; our code based occasionally needs a third parameter:
* allows using the optimal iteration more extensively and reduce allocations. * having this additional variant allows using the optimal iteration more extensively and
* reduce allocations.
*/ */
@Incubating @Incubating
<U,X> void fireEventOnEachListener(final U event, X param, final EventActionWithParameter<T,U,X> actionOnEvent); <U,X> void fireEventOnEachListener(U event, X param, EventActionWithParameter<T,U,X> actionOnEvent);
/** /**
* Similar to {@link #fireEventOnEachListener(Object, Function)}, but Reactive friendly: it chains * Similar to {@link #fireEventOnEachListener(Object, BiConsumer)}, but Reactive friendly:
* processing of the same event on each Reactive Listener, and returns a {@link CompletionStage} of type R. * it chains processing of the same event on each Reactive Listener, and returns a
* The various generic types allow using this for each concrete event type and flexible return types. * {@link CompletionStage} of type R. The various generic types allow using this for each
* <p>Used by Hibernate Reactive</p> * concrete event type and flexible return types.
* <p>
* <em>Used by Hibernate Reactive</em>
*
* @param event The event being fired * @param event The event being fired
* @param fun The function combining each event listener with the event * @param fun The function combining each event listener with the event
* @param <R> the return type of the returned CompletionStage * @param <R> the return type of the returned CompletionStage
* @param <U> the type of the event being fired on each listener * @param <U> the type of the event being fired on each listener
* @param <RL> the type of ReactiveListener: each listener of type T will be casted to it. * @param <RL> the type of ReactiveListener: each listener of type T will be cast to this type
* @return the composite completion stage of invoking fun(event) on each listener. * @return the composite completion stage of invoking fun(event) on each listener.
*/ */
@Incubating @Incubating
<R, U, RL> CompletionStage<R> fireEventOnEachListener(final U event, final Function<RL, Function<U, CompletionStage<R>>> fun); <R, U, RL> CompletionStage<R> fireEventOnEachListener(U event, Function<RL, Function<U, CompletionStage<R>>> fun);
/** /**
* Similar to {@link #fireEventOnEachListener(Object, Object, Function)}, but Reactive friendly: it chains * Similar to {@link #fireEventOnEachListener(Object, Object, EventActionWithParameter)},
* processing of the same event on each Reactive Listener, and returns a {@link CompletionStage} of type R. * but Reactive friendly: it chains processing of the same event on each Reactive Listener,
* The various generic types allow using this for each concrete event type and flexible return types. * and returns a {@link CompletionStage} of type R. The various generic types allow using
* <p>Used by Hibernate Reactive</p> * this for each concrete event type and flexible return types.
* <p>
* <em>Used by Hibernate Reactive</em>
*
* @param event The event being fired * @param event The event being fired
* @param fun The function combining each event listener with the event * @param fun The function combining each event listener with the event
* @param <R> the return type of the returned CompletionStage * @param <R> the return type of the returned CompletionStage
* @param <U> the type of the event being fired on each listener * @param <U> the type of the event being fired on each listener
* @param <RL> the type of ReactiveListener: each listener of type T will be casted to it. * @param <RL> the type of ReactiveListener: each listener of type T will be cast to this type
* @param <X> an additional parameter to be passed to the function fun * @param <X> an additional parameter to be passed to the function fun
* @return the composite completion stage of invoking fun(event) on each listener. * @return the composite completion stage of invoking fun(event) on each listener.
*/ */
@ -138,21 +147,26 @@ public interface EventListenerGroup<T> {
<R, U, RL, X> CompletionStage<R> fireEventOnEachListener(U event, X param, Function<RL, BiFunction<U, X, CompletionStage<R>>> fun); <R, U, RL, X> CompletionStage<R> fireEventOnEachListener(U event, X param, Function<RL, BiFunction<U, X, CompletionStage<R>>> fun);
/** /**
* Similar to {@link #fireLazyEventOnEachListener(Supplier, BiConsumer)}, but Reactive friendly: it chains * Similar to {@link #fireLazyEventOnEachListener(Supplier, BiConsumer)}, but Reactive
* processing of the same event on each Reactive Listener, and returns a {@link CompletionStage} of type R. * friendly: it chains processing of the same event on each Reactive Listener, and returns
* The various generic types allow using this for each concrete event type and flexible return types. * a {@link CompletionStage} of type R. The various generic types allow using this for
* <p>This variant expects a Supplier of the event, rather than the event directly; this is useful for the * each concrete event type and flexible return types.
* event types which are commonly configured with no listeners at all, so to allow skipping creating the * <p>
* event; use only for event types which are known to be expensive while the listeners are commonly empty.</p> * This variant expects a Supplier of the event, rather than the event directly; this is
* <p>Used by Hibernate Reactive</p> * useful for the event types which are commonly configured with no listeners at all, so
* to allow skipping creating the event; use only for event types which are known to be
* expensive while the listeners are commonly empty.
* <p>
* <em>Used by Hibernate Reactive</em>
*
* @param eventSupplier A supplier able to produce the actual event * @param eventSupplier A supplier able to produce the actual event
* @param fun The function combining each event listener with the event * @param fun The function combining each event listener with the event
* @param <R> the return type of the returned CompletionStage * @param <R> the return type of the returned CompletionStage
* @param <U> the type of the event being fired on each listener * @param <U> the type of the event being fired on each listener
* @param <RL> the type of ReactiveListener: each listener of type T will be casted to it. * @param <RL> the type of ReactiveListener: each listener of type T will be to this type
* @return the composite completion stage of invoking fun(event) on each listener. * @return the composite completion stage of invoking fun(event) on each listener.
*/ */
@Incubating @Incubating
<R, U, RL> CompletionStage<R> fireLazyEventOnEachListener(final Supplier<U> eventSupplier, final Function<RL, Function<U, CompletionStage<R>>> fun); <R, U, RL> CompletionStage<R> fireLazyEventOnEachListener(Supplier<U> eventSupplier, Function<RL, Function<U, CompletionStage<R>>> fun);
} }