Class ClosingFuture<V extends @Nullable java.lang.Object>

  • Type Parameters:
    V - the type of the value of this step

    @DoNotMock("Use ClosingFuture.from(Futures.immediate*Future)")
    public final class ClosingFuture<V extends @Nullable java.lang.Object>
    extends java.lang.Object
    A step in a pipeline of an asynchronous computation. When the last step in the computation is complete, some objects captured during the computation are closed.

    A pipeline of ClosingFutures is a tree of steps. Each step represents either an asynchronously-computed intermediate value, or else an exception that indicates the failure or cancellation of the operation so far. The only way to extract the value or exception from a step is by declaring that step to be the last step of the pipeline. Nevertheless, we refer to the "value" of a successful step or the "result" (value or exception) of any step.

    1. A pipeline starts at its leaf step (or steps), which is created from either a callable block or a ListenableFuture.
    2. Each other step is derived from one or more input steps. At each step, zero or more objects can be captured for later closing.
    3. There is one last step (the root of the tree), from which you can extract the final result of the computation. After that result is available (or the computation fails), all objects captured by any of the steps in the pipeline are closed.

    Starting a pipeline

    Start a ClosingFuture pipeline from a callable block that may capture objects for later closing. To start a pipeline from a ListenableFuture that doesn't create resources that should be closed later, you can use from(ListenableFuture) instead.

    Derived steps

    A ClosingFuture step can be derived from one or more input ClosingFuture steps in ways similar to FluentFutures:
    • by transforming the value from a successful input step,
    • by catching the exception from a failed input step, or
    • by combining the results of several input steps.
    Each derivation can capture the next value or any intermediate objects for later closing.

    A step can be the input to at most one derived step. Once you transform its value, catch its exception, or combine it with others, you cannot do anything else with it, including declare it to be the last step of the pipeline.

    Transforming

    To derive the next step by asynchronously applying a function to an input step's value, call transform(ClosingFunction, Executor) or transformAsync(AsyncClosingFunction, Executor) on the input step.

    Catching

    To derive the next step from a failed input step, call catching(Class, ClosingFunction, Executor) or catchingAsync(Class, AsyncClosingFunction, Executor) on the input step.

    Combining

    To derive a ClosingFuture from two or more input steps, pass the input steps to whenAllComplete(Iterable) or whenAllSucceed(Iterable) or its overloads.

    Cancelling

    Any step in a pipeline can be cancelled, even after another step has been derived, with the same semantics as cancelling a Future. In addition, a successfully cancelled step will immediately start closing all objects captured for later closing by it and by its input steps.

    Ending a pipeline

    Each ClosingFuture pipeline must be ended. To end a pipeline, decide whether you want to close the captured objects automatically or manually.

    Automatically closing

    You can extract a Future that represents the result of the last step in the pipeline by calling finishToFuture(). All objects the pipeline has captured for closing will begin to be closed asynchronously after the returned Future is done: the future completes before closing starts, rather than once it has finished.
    
     FluentFuture<UserName> userName =
         ClosingFuture.submit(
                 closer -> closer.eventuallyClose(database.newTransaction(), closingExecutor),
                 executor)
             .transformAsync((closer, transaction) -> transaction.queryClosingFuture("..."), executor)
             .transform((closer, result) -> result.get("userName"), directExecutor())
             .catching(DBException.class, e -> "no user", directExecutor())
             .finishToFuture();
     
    In this example, when the userName Future is done, the transaction and the query result cursor will both be closed, even if the operation is cancelled or fails.

    Manually closing

    If you want to close the captured objects manually, after you've used the final result, call finishToValueAndCloser(ValueAndCloserConsumer, Executor) to get an object that holds the final result. You then call ClosingFuture.ValueAndCloser.closeAsync() to close the captured objects.
    
         ClosingFuture.submit(
                 closer -> closer.eventuallyClose(database.newTransaction(), closingExecutor),
                 executor)
         .transformAsync((closer, transaction) -> transaction.queryClosingFuture("..."), executor)
         .transform((closer, result) -> result.get("userName"), directExecutor())
         .catching(DBException.class, e -> "no user", directExecutor())
         .finishToValueAndCloser(
             valueAndCloser -> this.userNameValueAndCloser = valueAndCloser, executor);
    
     // later
     try { // get() will throw if the operation failed or was cancelled.
       UserName userName = userNameValueAndCloser.get();
       // do something with userName
     } finally {
       userNameValueAndCloser.closeAsync();
     }
     
    In this example, when userNameValueAndCloser.closeAsync() is called, the transaction and the query result cursor will both be closed, even if the operation is cancelled or fails.

    Note that if you don't call closeAsync(), the captured objects will not be closed. The automatic-closing approach described above is safer.

    Since:
    30.0
    • Method Detail

      • submit

        public static <V extends @Nullable java.lang.Object> ClosingFuture<V> submit​(ClosingFuture.ClosingCallable<V> callable,
                                                                                     java.util.concurrent.Executor executor)
        Starts a ClosingFuture pipeline by submitting a callable block to an executor.
        Throws:
        java.util.concurrent.RejectedExecutionException - if the task cannot be scheduled for execution
      • whenAllSucceed

        public static ClosingFuture.Combiner whenAllSucceed​(java.lang.Iterable<? extends ClosingFuture<?>> futures)
        Starts specifying how to combine ClosingFutures into a single pipeline, assuming they all succeed. If any fail, the resulting pipeline will fail.
        Throws:
        java.lang.IllegalStateException - if a ClosingFuture has already been derived from any of the futures, or if any has already been finished
      • whenAllSucceed

        public static <V1 extends @Nullable java.lang.Object,​V2 extends @Nullable java.lang.Object> ClosingFuture.Combiner2<V1,​V2> whenAllSucceed​(ClosingFuture<V1> future1,
                                                                                                                                                              ClosingFuture<V2> future2)
        Starts specifying how to combine two ClosingFutures into a single pipeline, assuming they all succeed. If any fail, the resulting pipeline will fail.

        Calling this method allows you to use lambdas or method references typed with the types of the input ClosingFutures.

        Throws:
        java.lang.IllegalStateException - if a ClosingFuture has already been derived from any of the arguments, or if any has already been finished
      • whenAllSucceed

        public static <V1 extends @Nullable java.lang.Object,​V2 extends @Nullable java.lang.Object,​V3 extends @Nullable java.lang.Object> ClosingFuture.Combiner3<V1,​V2,​V3> whenAllSucceed​(ClosingFuture<V1> future1,
                                                                                                                                                                                                                   ClosingFuture<V2> future2,
                                                                                                                                                                                                                   ClosingFuture<V3> future3)
        Starts specifying how to combine three ClosingFutures into a single pipeline, assuming they all succeed. If any fail, the resulting pipeline will fail.

        Calling this method allows you to use lambdas or method references typed with the types of the input ClosingFutures.

        Throws:
        java.lang.IllegalStateException - if a ClosingFuture has already been derived from any of the arguments, or if any has already been finished
      • statusFuture

        public ListenableFuture<?> statusFuture()
        Returns a future that finishes when this step does. Calling get() on the returned future returns null if the step is successful or throws the same exception that would be thrown by calling finishToFuture().get() if this were the last step. Calling cancel() on the returned future has no effect on the ClosingFuture pipeline.

        statusFuture differs from most methods on ClosingFuture: You can make calls to statusFuture in addition to the call you make to finishToFuture() or a derivation method on the same instance. This is important because calling statusFuture alone does not provide a way to close the pipeline.

      • transform

        public <U extends @Nullable java.lang.Object> ClosingFuture<U> transform​(ClosingFuture.ClosingFunction<? super V,​U> function,
                                                                                 java.util.concurrent.Executor executor)
        Returns a new ClosingFuture pipeline step derived from this one by applying a function to its value. The function can use a ClosingFuture.DeferredCloser to capture objects to be closed when the pipeline is done.

        If this ClosingFuture fails, the function will not be called, and the derived ClosingFuture will be equivalent to this one.

        If the function throws an exception, that exception is used as the result of the derived ClosingFuture.

        Example usage:

        
         ClosingFuture<List<Row>> rowsFuture =
             queryFuture.transform((closer, result) -> result.getRows(), executor);
         

        When selecting an executor, note that directExecutor is dangerous in some cases. See the discussion in the ListenableFuture.addListener(java.lang.Runnable, java.util.concurrent.Executor) documentation. All its warnings about heavyweight listeners are also applicable to heavyweight functions passed to this method.

        After calling this method, you may not call finishToFuture(), finishToValueAndCloser(ValueAndCloserConsumer, Executor), or any other derivation method on the original ClosingFuture instance.

        Parameters:
        function - transforms the value of this step to the value of the derived step
        executor - executor to run the function in
        Returns:
        the derived step
        Throws:
        java.lang.IllegalStateException - if a ClosingFuture has already been derived from this one, or if this ClosingFuture has already been finished
      • transformAsync

        public <U extends @Nullable java.lang.Object> ClosingFuture<U> transformAsync​(ClosingFuture.AsyncClosingFunction<? super V,​U> function,
                                                                                      java.util.concurrent.Executor executor)
        Returns a new ClosingFuture pipeline step derived from this one by applying a function that returns a ClosingFuture to its value. The function can use a ClosingFuture.DeferredCloser to capture objects to be closed when the pipeline is done (other than those captured by the returned ClosingFuture).

        If this ClosingFuture succeeds, the derived one will be equivalent to the one returned by the function.

        If this ClosingFuture fails, the function will not be called, and the derived ClosingFuture will be equivalent to this one.

        If the function throws an exception, that exception is used as the result of the derived ClosingFuture. But if the exception is thrown after the function creates a ClosingFuture, then none of the closeable objects in that ClosingFuture will be closed.

        Usage guidelines for this method:

        Example usage:

        
         // Result.getRowsClosingFuture() returns a ClosingFuture.
         ClosingFuture<List<Row>> rowsFuture =
             queryFuture.transformAsync((closer, result) -> result.getRowsClosingFuture(), executor);
        
         // Result.writeRowsToOutputStreamFuture() returns a ListenableFuture that resolves to the
         // number of written rows. openOutputFile() returns a FileOutputStream (which implements
         // Closeable).
         ClosingFuture<Integer> rowsFuture2 =
             queryFuture.transformAsync(
                 (closer, result) -> {
                   FileOutputStream fos = closer.eventuallyClose(openOutputFile(), closingExecutor);
                   return ClosingFuture.from(result.writeRowsToOutputStreamFuture(fos));
              },
              executor);
        
         // Result.getRowsFuture() returns a ListenableFuture (no new closeables are created).
         ClosingFuture<List<Row>> rowsFuture3 =
             queryFuture.transformAsync(withoutCloser(Result::getRowsFuture), executor);
        
         

        When selecting an executor, note that directExecutor is dangerous in some cases. See the discussion in the ListenableFuture.addListener(java.lang.Runnable, java.util.concurrent.Executor) documentation. All its warnings about heavyweight listeners are also applicable to heavyweight functions passed to this method. (Specifically, directExecutor functions should avoid heavyweight operations inside AsyncClosingFunction.apply. Any heavyweight operations should occur in other threads responsible for completing the returned ClosingFuture.)

        After calling this method, you may not call finishToFuture(), finishToValueAndCloser(ValueAndCloserConsumer, Executor), or any other derivation method on the original ClosingFuture instance.

        Parameters:
        function - transforms the value of this step to a ClosingFuture with the value of the derived step
        executor - executor to run the function in
        Returns:
        the derived step
        Throws:
        java.lang.IllegalStateException - if a ClosingFuture has already been derived from this one, or if this ClosingFuture has already been finished
      • catching

        public <X extends java.lang.Throwable> ClosingFuture<Vcatching​(java.lang.Class<X> exceptionType,
                                                                         ClosingFuture.ClosingFunction<? super X,​? extends V> fallback,
                                                                         java.util.concurrent.Executor executor)
        Returns a new ClosingFuture pipeline step derived from this one by applying a function to its exception if it is an instance of a given exception type. The function can use a ClosingFuture.DeferredCloser to capture objects to be closed when the pipeline is done.

        If this ClosingFuture succeeds or fails with a different exception type, the function will not be called, and the derived ClosingFuture will be equivalent to this one.

        If the function throws an exception, that exception is used as the result of the derived ClosingFuture.

        Example usage:

        
         ClosingFuture<QueryResult> queryFuture =
             queryFuture.catching(
                 QueryException.class, (closer, x) -> Query.emptyQueryResult(), executor);
         

        When selecting an executor, note that directExecutor is dangerous in some cases. See the discussion in the ListenableFuture.addListener(java.lang.Runnable, java.util.concurrent.Executor) documentation. All its warnings about heavyweight listeners are also applicable to heavyweight functions passed to this method.

        After calling this method, you may not call finishToFuture(), finishToValueAndCloser(ValueAndCloserConsumer, Executor), or any other derivation method on the original ClosingFuture instance.

        Parameters:
        exceptionType - the exception type that triggers use of fallback. The exception type is matched against this step's exception. "This step's exception" means the cause of the ExecutionException thrown by Future.get() on the Future underlying this step or, if get() throws a different kind of exception, that exception itself. To avoid hiding bugs and other unrecoverable errors, callers should prefer more specific types, avoiding Throwable.class in particular.
        fallback - the function to be called if this step fails with the expected exception type. The function's argument is this step's exception. "This step's exception" means the cause of the ExecutionException thrown by Future.get() on the Future underlying this step or, if get() throws a different kind of exception, that exception itself.
        executor - the executor that runs fallback if the input fails
      • catchingAsync

        public <X extends java.lang.Throwable> ClosingFuture<VcatchingAsync​(java.lang.Class<X> exceptionType,
                                                                              ClosingFuture.AsyncClosingFunction<? super X,​? extends V> fallback,
                                                                              java.util.concurrent.Executor executor)
        Returns a new ClosingFuture pipeline step derived from this one by applying a function that returns a ClosingFuture to its exception if it is an instance of a given exception type. The function can use a ClosingFuture.DeferredCloser to capture objects to be closed when the pipeline is done (other than those captured by the returned ClosingFuture).

        If this ClosingFuture fails with an exception of the given type, the derived ClosingFuture will be equivalent to the one returned by the function.

        If this ClosingFuture succeeds or fails with a different exception type, the function will not be called, and the derived ClosingFuture will be equivalent to this one.

        If the function throws an exception, that exception is used as the result of the derived ClosingFuture. But if the exception is thrown after the function creates a ClosingFuture, then none of the closeable objects in that ClosingFuture will be closed.

        Usage guidelines for this method:

        Example usage:

        
         // Fall back to a secondary input stream in case of IOException.
         ClosingFuture<InputStream> inputFuture =
             firstInputFuture.catchingAsync(
                 IOException.class, (closer, x) -> secondaryInputStreamClosingFuture(), executor);
         
         }

        When selecting an executor, note that directExecutor is dangerous in some cases. See the discussion in the ListenableFuture.addListener(java.lang.Runnable, java.util.concurrent.Executor) documentation. All its warnings about heavyweight listeners are also applicable to heavyweight functions passed to this method. (Specifically, directExecutor functions should avoid heavyweight operations inside AsyncClosingFunction.apply. Any heavyweight operations should occur in other threads responsible for completing the returned ClosingFuture.)

        After calling this method, you may not call finishToFuture(), finishToValueAndCloser(ValueAndCloserConsumer, Executor), or any other derivation method on the original ClosingFuture instance.

        Parameters:
        exceptionType - the exception type that triggers use of fallback. The exception type is matched against this step's exception. "This step's exception" means the cause of the ExecutionException thrown by Future.get() on the Future underlying this step or, if get() throws a different kind of exception, that exception itself. To avoid hiding bugs and other unrecoverable errors, callers should prefer more specific types, avoiding Throwable.class in particular.
        fallback - the function to be called if this step fails with the expected exception type. The function's argument is this step's exception. "This step's exception" means the cause of the ExecutionException thrown by Future.get() on the Future underlying this step or, if get() throws a different kind of exception, that exception itself.
        executor - the executor that runs fallback if the input fails
      • finishToFuture

        public FluentFuture<VfinishToFuture()
        Marks this step as the last step in the ClosingFuture pipeline.

        The returned Future is completed when the pipeline's computation completes, or when the pipeline is cancelled.

        All objects the pipeline has captured for closing will begin to be closed asynchronously after the returned Future is done: the future completes before closing starts, rather than once it has finished.

        After calling this method, you may not call finishToValueAndCloser(ValueAndCloserConsumer, Executor), this method, or any other derivation method on the original ClosingFuture instance.

        Returns:
        a Future that represents the final value or exception of the pipeline
      • finishToValueAndCloser

        public void finishToValueAndCloser​(ClosingFuture.ValueAndCloserConsumer<? super V> consumer,
                                           java.util.concurrent.Executor executor)
        Marks this step as the last step in the ClosingFuture pipeline. When this step is done, receiver will be called with an object that contains the result of the operation. The receiver can store the ClosingFuture.ValueAndCloser outside the receiver for later synchronous use.

        After calling this method, you may not call finishToFuture(), this method again, or any other derivation method on the original ClosingFuture instance.

        Parameters:
        consumer - a callback whose method will be called (using executor) when this operation is done
      • cancel

        @CanIgnoreReturnValue
        public boolean cancel​(boolean mayInterruptIfRunning)
        Attempts to cancel execution of this step. This attempt will fail if the step has already completed, has already been cancelled, or could not be cancelled for some other reason. If successful, and this step has not started when cancel is called, this step should never run.

        If successful, causes the objects captured by this step (if already started) and its input step(s) for later closing to be closed on their respective Executors. If any such calls specified MoreExecutors.directExecutor(), those objects will be closed synchronously.

        Parameters:
        mayInterruptIfRunning - true if the thread executing this task should be interrupted; otherwise, in-progress tasks are allowed to complete, but the step will be cancelled regardless
        Returns:
        false if the step could not be cancelled, typically because it has already completed normally; true otherwise
      • toString

        public java.lang.String toString()
        Overrides:
        toString in class java.lang.Object
      • finalize

        protected void finalize()
        Overrides:
        finalize in class java.lang.Object