Class RefCountingListener

java.lang.Object
org.elasticsearch.action.support.RefCountingListener
All Implemented Interfaces:
Closeable, AutoCloseable, Releasable

public final class RefCountingListener extends Object implements Releasable
A mechanism to complete a listener on the completion of some (dynamic) collection of other actions. Basic usage is as follows:
 try (var refs = new RefCountingListener(finalListener)) {
     for (var item : collection) {
         runAsyncAction(item, refs.acquire()); // completes the acquired listener on completion
     }
 }
 
The delegate listener is completed when execution leaves the try-with-resources block and every acquired reference is released. The RefCountingListener collects (a bounded number of) exceptions received by its subsidiary listeners, and completes the delegate listener with an exception if (and only if) any subsidiary listener fails. However, unlike a GroupedActionListener it leaves it to the caller to collect the results of successful completions by accumulating them in a data structure of its choice. Also unlike a GroupedActionListener there is no need to declare the number of subsidiary listeners up front: listeners can be acquired dynamically as needed. Finally, you can continue to acquire additional listeners even outside the try-with-resources block, perhaps in a separate thread, as long as there's at least one listener outstanding:
 try (var refs = new RefCountingListener(finalListener)) {
     for (var item : collection) {
         if (condition(item)) {
             runAsyncAction(item, refs.acquire(results::add));
         }
     }
     if (flag) {
         runOneOffAsyncAction(refs.acquire(results::add));
         return;
     }
     for (var item : otherCollection) {
         var itemRef = refs.acquire(); // delays completion while the background action is pending
         executorService.execute(() -> {
             try {
                 if (condition(item)) {
                     runOtherAsyncAction(item, refs.acquire(results::add));
                 }
             } finally {
                 itemRef.onResponse(null);
             }
         });
     }
 }
 
In particular (and also unlike a GroupedActionListener) this works even if you don't acquire any extra refs at all: in that case, the delegate listener is completed at the end of the try-with-resources block.
  • Constructor Details

    • RefCountingListener

      public RefCountingListener(ActionListener<Void> delegate)
      Construct a RefCountingListener which completes delegate when all refs are released.
      Parameters:
      delegate - The listener to complete when all refs are released. 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 with failure then the delegate is completed with the first exception received, with other exceptions added to its collection of suppressed exceptions.
    • RefCountingListener

      public RefCountingListener(int maxExceptions, ActionListener<Void> delegate)
      Construct a RefCountingListener which completes delegate when all refs are released.
      Parameters:
      delegate - The listener to complete when all refs are released. 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 with failure then the delegate is completed with the first exception received, with other exceptions added to its collection of suppressed exceptions.
      maxExceptions - The maximum number of exceptions to accumulate on failure.
  • Method Details

    • close

      public void close()
      Release the original reference to this object, which completes the delegate ActionListener if there are no other references. 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:
      close in interface AutoCloseable
      Specified by:
      close in interface Closeable
      Specified by:
      close in interface Releasable
    • acquire

      public ActionListener<Void> acquire()
      Acquire a reference to this object and return a listener which releases it. The delegate ActionListener is called when all its references have been released. It is invalid to call this method once all references are released. Doing so will trip an assertion if assertions are enabled, and will throw an IllegalStateException otherwise. 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

      public <Response> ActionListener<Response> acquire(CheckedConsumer<Response,Exception> consumer)
      Acquire a reference to this object and return a listener which consumes a response and releases the reference. The delegate ActionListener is called when all its references have been released. If the consumer throws an exception, the exception is passed to the final listener as if the returned listener was completed exceptionally. It is invalid to call this method once all references are released. Doing so will trip an assertion if assertions are enabled, and will throw an IllegalStateException otherwise. 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

      public String toString()
      Overrides:
      toString in class Object
    • isFailing

      public boolean isFailing()
      Returns:
      true if at least one acquired listener has completed exceptionally, which means that the delegate listener will also complete exceptionally once all acquired listeners are completed.