- All Implemented Interfaces:
Closeable,AutoCloseable,Releasable
try (var listeners = new RefCountingListener(finalListener)) {
for (var item : collection) {
runAsyncAction(item, listeners.acquire()); // completes the acquired listener on completion
}
}
The delegate listener is completed when execution leaves the try-with-resources block and every acquired listener is completed. The
RefCountingListener collects a bounded number of the exceptions received by the acquired listeners, and completes the delegate
listener with an exception if and only if any acquired listener fails. If more than one acquired listener fails, the resulting exception
is the first such failure, to which other tracked exceptions are added using Throwable.addSuppressed(java.lang.Throwable).
A RefCountingListener, unlike a GroupedActionListener, leaves it to the caller to collect the results of successful
completions by accumulating them in a data structure of its choice: a RefCountingListener itself adds only a small and
O(1) amount of heap overhead. Also unlike a GroupedActionListener there is no need to declare the number of subsidiary
listeners up front: listeners can be acquired dynamically as needed, and you can continue to acquire additional listeners outside the
try-with-resources block, even in a separate thread, as long as you ensure there's at least one listener outstanding:
try (var listeners = new RefCountingListener(finalListener)) {
for (var item : collection) {
if (condition(item)) {
runAsyncAction(item, listeners.acquire(results::add));
}
}
if (flag) {
runOneOffAsyncAction(listeners.acquire(results::add));
return;
}
for (var item : otherCollection) {
var itemListener = listeners.acquire(); // delays completion while the background action is pending
executorService.execute(() -> {
try {
if (condition(item)) {
runOtherAsyncAction(item, listeners.acquire(results::add));
}
} finally {
itemListener.onResponse(null);
}
});
}
}
In particular (and also unlike a GroupedActionListener) this works even if you don't acquire any extra listeners at all: in that
case, the delegate listener is completed at the end of the try-with-resources block.
The delegate listener is completed on the thread that completes the last acquired listener, or the thread that closes the try-with-resources block if there are no incomplete acquired listeners when this happens.
See also RefCountingRunnable, which fulfils a similar role in situations where the subsidiary actions cannot fail (or at least
where you do not need such failures to propagate automatically to the final action).
-
Constructor Summary
ConstructorsConstructorDescriptionRefCountingListener(int maxExceptions, ActionListener<Void> delegate) Construct aRefCountingListenerwhich completesdelegatewhen all acquired listeners have been completed.RefCountingListener(ActionListener<Void> delegate) Construct aRefCountingListenerwhich completesdelegatewhen all acquired listeners have been completed. -
Method Summary
Modifier and TypeMethodDescriptionacquire()Acquire a listener which awaits aVoidresponse.<Response> ActionListener<Response> acquire(CheckedConsumer<Response, Exception> consumer) Acquire a listener which consumes a response, for instance by storing the response in some kind of data structure.voidclose()Release the original reference to this object, which completes the delegateActionListenerif there are no incomplete acquired listeners.booleantoString()
-
Constructor Details
-
RefCountingListener
Construct aRefCountingListenerwhich completesdelegatewhen all acquired listeners have been completed.- Parameters:
delegate- The listener to complete when all acquired listeners are completed. This listener must not throw any exception on completion. If all the acquired listeners completed successfully then so is the delegate. If any of the acquired listeners completed exceptionally then the delegate is completed with the first exception received, with up to 10 other exceptions added to its collection of suppressed exceptions.
-
RefCountingListener
Construct aRefCountingListenerwhich completesdelegatewhen all acquired listeners have been completed.- Parameters:
maxExceptions- The maximum number of exceptions to accumulate on failure.delegate- The listener to complete when all acquired listeners are completed. This listener must not throw any exception on completion. If all the acquired listeners completed successfully then so is the delegate. If any of the acquired listeners completed exceptionally then the delegate is completed with the first exception received, with other exceptions added to its collection of suppressed exceptions.
-
-
Method Details
-
close
public void close()Release the original reference to this object, which completes the delegateActionListenerif there are no incomplete acquired listeners.It is invalid to call this method more than once. Doing so will trip an assertion if assertions are enabled, but will be ignored otherwise. This deviates from the contract of
Closeable.- Specified by:
closein interfaceAutoCloseable- Specified by:
closein interfaceCloseable- Specified by:
closein interfaceReleasable
-
acquire
Acquire a listener which awaits aVoidresponse. The delegateActionListeneris called when all such acquired listeners are completed, on the thread that completes the last such listener.It is invalid to call this method once all acquired listeners have been completed and the original try-with-resources block is closed. Doing so will trip an assertion if assertions are enabled, and will throw an
IllegalStateExceptionotherwise.It is also invalid to complete the returned listener more than once. Doing so will trip an assertion if assertions are enabled, but will be ignored otherwise.
-
acquire
Acquire a listener which consumes a response, for instance by storing the response in some kind of data structure. The delegateActionListeneris called when all such acquired listeners are completed, on the thread that completes the last such listener. If theconsumerthrows an exception then that exception is passed to the final listener as if the returned listener was completed exceptionally.It is invalid to call this method once all acquired listeners have been completed and the original try-with-resources block is closed. Doing so will trip an assertion if assertions are enabled, and will throw an
IllegalStateExceptionotherwise.It is also invalid to complete the returned listener more than once. Doing so will trip an assertion if assertions are enabled, but will be ignored otherwise.
-
toString
-
isFailing
public boolean isFailing()- Returns:
trueif at least one acquired listener has completed exceptionally, which means that the delegate listener will also complete exceptionally once all acquired listeners are completed.
-