Class SubscribableListener<T>
- All Implemented Interfaces:
ActionListener<T>
- Direct Known Subclasses:
ListenableActionFuture
,ListenableFuture
ActionListener
to which other ActionListener
instances can subscribe, such that when this listener is completed it
fans-out its result to the subscribed listeners.
Exceptions are passed to subscribed listeners without modification. ListenableActionFuture
and ListenableFuture
are child
classes that provide additional exception handling.
A sequence of async steps can be chained together using a series of SubscribableListener
s, similar to CompletionStage
(without the catch (Throwable t)
). Listeners can be created for each step, where the next step subscribes to the result of the
previous, using utilities like andThen(CheckedBiConsumer)
. The following example demonstrates how this might be used:
private void exampleAsyncMethod(String request, List<Long> items, ActionListener<Boolean> finalListener) {
SubscribableListener
// Start the chain and run the first step by creating a SubscribableListener using newForked():
.<String>newForked(l -> firstAsyncStep(request, l))
// Run a second step when the first step completes using andThen(); if the first step fails then the exception falls through to
// the end without executing the intervening steps.
.<Integer>andThen((l, firstStepResult) -> secondAsyncStep(request, firstStepResult, l))
// Run another step when the second step completes with another andThen() call; as above this only runs if the first two steps
// succeed.
.<Boolean>andThen((l, secondStepResult) -> {
if (condition) {
// Steps are exception-safe: an exception thrown here will be passed to the listener rather than escaping to the
// caller.
throw new IOException("failure");
}
// Steps can fan out to multiple subsidiary async actions using utilities like RefCountingListener.
final var result = new AtomicBoolean();
try (var listeners = new RefCountingListener(l.map(v -> result.get()))) {
for (final var item : items) {
thirdAsyncStep(secondStepResult, item, listeners.acquire());
}
}
})
// Synchronous (non-forking) steps which do not return a result can be expressed using andThenAccept() with a consumer:
.andThenAccept(thirdStepResult -> {
if (condition) {
// andThenAccept() is also exception-safe
throw new ElasticsearchException("some other problem");
}
consumeThirdStepResult(thirdStepResult);
})
// Synchronous (non-forking) steps which do return a result can be expressed using andThenApply() with a function:
.andThenApply(voidFromStep4 -> {
if (condition) {
// andThenApply() is also exception-safe
throw new IllegalArgumentException("failure");
}
return computeFifthStepResult();
})
// To complete the chain, add the outer listener which will be completed with the result of the previous step if all steps were
// successful, or the exception if any step failed.
.addListener(finalListener);
}
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionfinal void
addListener
(ActionListener<T> listener) Add a listener to this listener's collection of subscribers.final void
addListener
(ActionListener<T> listener, Executor executor, ThreadContext threadContext) Add a listener to this listener's collection of subscribers.void
addTimeout
(TimeValue timeout, ThreadPool threadPool, Executor timeoutExecutor) Adds a timeout to this listener, such that if the timeout elapses before the listener is completed then it will be completed with anElasticsearchTimeoutException
.<U> SubscribableListener<U>
andThen
(Executor executor, ThreadContext threadContext, CheckedBiConsumer<ActionListener<U>, T, ? extends Exception> nextStep) Creates and returns a newSubscribableListener
L
and subscribesnextStep
to this listener such that if this listener is completed successfully with resultR
thennextStep
is invoked with argumentsL
andR
.<U> SubscribableListener<U>
andThen
(CheckedBiConsumer<ActionListener<U>, T, ? extends Exception> nextStep) Creates and returns a newSubscribableListener
L
and subscribesnextStep
to this listener such that if this listener is completed successfully with resultR
thennextStep
is invoked with argumentsL
andR
.andThenAccept
(CheckedConsumer<T, Exception> consumer) Creates and returns a newSubscribableListener
L
such that if this listener is completed successfully with resultR
thenconsumer
is applied to argumentR
, andL
is completed withnull
whenconsumer
returns.<U> SubscribableListener<U>
andThenApply
(CheckedFunction<T, U, Exception> fn) Creates and returns a newSubscribableListener
L
such that if this listener is completed successfully with resultR
thenfn
is invoked with argumentR
, andL
is completed with the result of that invocation.final boolean
isDone()
static <T> SubscribableListener<T>
Create aSubscribableListener
which has already failed with the given exception.static <T> SubscribableListener<T>
newForked
(CheckedConsumer<ActionListener<T>, ? extends Exception> fork) Create aSubscribableListener
, fork a computation to complete it, and return the listener.static <T> SubscribableListener<T>
newSucceeded
(T result) Create aSubscribableListener
which has already succeeded with the given result.final void
A failure caused by an exception at some phase of the task.final void
onResponse
(T result) Handle action response.protected final T
protected static RuntimeException
protected Exception
wrapException
(Exception exception) Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
Methods inherited from interface org.elasticsearch.action.ActionListener
delegateFailure, delegateFailureAndWrap, delegateResponse, map, safeMap
-
Constructor Details
-
SubscribableListener
public SubscribableListener()Create aSubscribableListener
which is incomplete.
-
-
Method Details
-
newSucceeded
Create aSubscribableListener
which has already succeeded with the given result. -
newFailed
Create aSubscribableListener
which has already failed with the given exception. -
newForked
public static <T> SubscribableListener<T> newForked(CheckedConsumer<ActionListener<T>, ? extends Exception> fork) Create aSubscribableListener
, fork a computation to complete it, and return the listener. If the forking itself throws an exception then the exception is caught and fed to the returned listener. -
addListener
Add a listener to this listener's collection of subscribers. If this listener is complete, this method completes the subscribing listener immediately with the result with which this listener was completed. Otherwise, the subscribing listener is retained and completed when this listener is completed.Subscribed listeners must not throw any exceptions.
Listeners added strictly before this listener is completed will themselves be completed in the order in which their subscriptions were received. However, there are no guarantees about the ordering of the completions of listeners which are added concurrently with (or after) the completion of this listener.
If the subscribed listener is not completed immediately then it will be completed on the thread, and in the
ThreadContext
, of the thread which completes this listener. -
addListener
public final void addListener(ActionListener<T> listener, Executor executor, @Nullable ThreadContext threadContext) Add a listener to this listener's collection of subscribers. If this listener is complete, this method completes the subscribing listener immediately with the result with which this listener was completed. Otherwise, the subscribing listener is retained and completed when this listener is completed.Subscribed listeners must not throw any exceptions.
Listeners added strictly before this listener is completed will themselves be completed in the order in which their subscriptions were received. However, there are no guarantees about the ordering of the completions of listeners which are added concurrently with (or after) the completion of this listener.
- Parameters:
executor
- If notEsExecutors.DIRECT_EXECUTOR_SERVICE
, and the subscribing listener is not completed immediately, then it will be completed using the given executor. If the subscribing listener is completed immediately then this completion happens on the subscribing thread.threadContext
- If notnull
, and the subscribing listener is not completed immediately, then it will be completed in the given thread context. Ifnull
, and the subscribing listener is not completed immediately, then it will be completed in theThreadContext
of the completing thread. If the subscribing listener is completed immediately then this completion happens in theThreadContext
of the subscribing thread.
-
onResponse
Description copied from interface:ActionListener
Handle action response. This response may constitute a failure or a success but it is up to the listener to make that decision.- Specified by:
onResponse
in interfaceActionListener<T>
-
onFailure
Description copied from interface:ActionListener
A failure caused by an exception at some phase of the task.- Specified by:
onFailure
in interfaceActionListener<T>
-
wrapException
-
isDone
public final boolean isDone()- Returns:
true
if and only if this listener has been completed (either successfully or exceptionally).
-
rawResult
- Returns:
- the result with which this listener completed successfully, or throw the exception with which it failed.
- Throws:
AssertionError
- if this listener is not complete yet and assertions are enabled.IllegalStateException
- if this listener is not complete yet and assertions are disabled.Exception
-
wrapAsExecutionException
-
andThen
public <U> SubscribableListener<U> andThen(CheckedBiConsumer<ActionListener<U>, T, ? extends Exception> nextStep) Creates and returns a newSubscribableListener
L
and subscribesnextStep
to this listener such that if this listener is completed successfully with resultR
thennextStep
is invoked with argumentsL
andR
. If this listener is completed with exceptionE
then so isL
.This can be used to construct a sequence of async actions, each invoked with the result of the previous one:
l.andThen((l1, o1) -> forkAction1(o1, args1, l1)).andThen((l2, o2) -> forkAction2(o2, args2, l2)).addListener(finalListener);
After creating this chain, completingl
with a successful response will pass the response toforkAction1
, which will on completion pass its response toforkAction2
, which will in turn pass its response tofinalListener
. A failure of any step will bypass the remaining steps and ultimately failfinalListener
.The threading of the
nextStep
callback is the same as for listeners added withaddListener(org.elasticsearch.action.ActionListener<T>)
: if this listener is already complete thennextStep
is invoked on the thread callingandThen(org.elasticsearch.common.CheckedBiConsumer<org.elasticsearch.action.ActionListener<U>, T, ? extends java.lang.Exception>)
and in its thread context, but if this listener is incomplete thennextStep
is invoked on the completing thread and in its thread context. -
andThen
public <U> SubscribableListener<U> andThen(Executor executor, @Nullable ThreadContext threadContext, CheckedBiConsumer<ActionListener<U>, T, ? extends Exception> nextStep) Creates and returns a newSubscribableListener
L
and subscribesnextStep
to this listener such that if this listener is completed successfully with resultR
thennextStep
is invoked with argumentsL
andR
. If this listener is completed with exceptionE
then so isL
.This can be used to construct a sequence of async actions, each invoked with the result of the previous one:
l.andThen(x, t, (l1,o1) -> forkAction1(o1,args1,l1)).andThen(x, t, (l2,o2) -> forkAction2(o2,args2,l2)).addListener(finalListener);
After creating this chain, completingl
with a successful response will pass the response toforkAction1
, which will on completion pass its response toforkAction2
, which will in turn pass its response tofinalListener
. A failure of any step will bypass the remaining steps and ultimately failfinalListener
.The threading of the
nextStep
callback is the same as for listeners added withaddListener(org.elasticsearch.action.ActionListener<T>)
: if this listener is already complete thennextStep
is invoked on the thread callingandThen(org.elasticsearch.common.CheckedBiConsumer<org.elasticsearch.action.ActionListener<U>, T, ? extends java.lang.Exception>)
and in its thread context, but if this listener is incomplete thennextStep
is invoked usingexecutor
, in a thread context captured whenandThen(org.elasticsearch.common.CheckedBiConsumer<org.elasticsearch.action.ActionListener<U>, T, ? extends java.lang.Exception>)
was called. -
andThenApply
Creates and returns a newSubscribableListener
L
such that if this listener is completed successfully with resultR
thenfn
is invoked with argumentR
, andL
is completed with the result of that invocation. If this listener is completed exceptionally, orfn
throws an exception, thenL
is completed with that exception.This is essentially a shorthand for a call to
andThen(org.elasticsearch.common.CheckedBiConsumer<org.elasticsearch.action.ActionListener<U>, T, ? extends java.lang.Exception>)
with anextStep
argument that is fully synchronous.The threading of the
fn
invocation is the same as for listeners added withaddListener(org.elasticsearch.action.ActionListener<T>)
: if this listener is already complete thenfn
is invoked on the thread callingandThenApply(org.elasticsearch.core.CheckedFunction<T, U, java.lang.Exception>)
and in its thread context, but if this listener is incomplete thenfn
is invoked on the thread, and in the thread context, on which this listener is completed. -
andThenAccept
Creates and returns a newSubscribableListener
L
such that if this listener is completed successfully with resultR
thenconsumer
is applied to argumentR
, andL
is completed withnull
whenconsumer
returns. If this listener is completed exceptionally, orconsumer
throws an exception, thenL
is completed with that exception.This is essentially a shorthand for a call to
andThen(org.elasticsearch.common.CheckedBiConsumer<org.elasticsearch.action.ActionListener<U>, T, ? extends java.lang.Exception>)
with anextStep
argument that is fully synchronous.The threading of the
consumer
invocation is the same as for listeners added withaddListener(org.elasticsearch.action.ActionListener<T>)
: if this listener is already complete thenconsumer
is invoked on the thread callingandThenAccept(org.elasticsearch.core.CheckedConsumer<T, java.lang.Exception>)
and in its thread context, but if this listener is incomplete thenconsumer
is invoked on the thread, and in the thread context, on which this listener is completed. -
addTimeout
Adds a timeout to this listener, such that if the timeout elapses before the listener is completed then it will be completed with anElasticsearchTimeoutException
.The process which is racing against this timeout should stop and clean up promptly when the timeout occurs to avoid unnecessary work. For instance, it could check that the race is not lost by calling
isDone()
whenever appropriate, or it could subscribe another listener which performs any necessary cleanup steps.
-