All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.driver.internal.util.Futures Maven / Gradle / Ivy

/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [http://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.neo4j.driver.internal.util;

import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;

import org.neo4j.driver.internal.async.connection.EventLoopGroupFactory;

import static java.util.concurrent.CompletableFuture.completedFuture;
import static org.neo4j.driver.internal.util.ErrorUtil.addSuppressed;

public final class Futures
{
    private static final CompletableFuture COMPLETED_WITH_NULL = completedFuture( null );

    private Futures()
    {
    }

    @SuppressWarnings( "unchecked" )
    public static  CompletableFuture completedWithNull()
    {
        return (CompletableFuture) COMPLETED_WITH_NULL;
    }

    public static  CompletableFuture completeWithNullIfNoError( CompletableFuture future, Throwable error )
    {
        if ( error != null )
        {
            future.completeExceptionally( error );
        }
        else
        {
            future.complete( null );
        }
        return future;
    }

    public static  CompletionStage asCompletionStage( io.netty.util.concurrent.Future future )
    {
        CompletableFuture result = new CompletableFuture<>();
        return asCompletionStage( future, result );
    }

    public static  CompletionStage asCompletionStage( io.netty.util.concurrent.Future future, CompletableFuture result )
    {
        if ( future.isCancelled() )
        {
            result.cancel( true );
        }
        else if ( future.isSuccess() )
        {
            result.complete( future.getNow() );
        }
        else if ( future.cause() != null )
        {
            result.completeExceptionally( future.cause() );
        }
        else
        {
            future.addListener( ignore ->
            {
                if ( future.isCancelled() )
                {
                    result.cancel( true );
                }
                else if ( future.isSuccess() )
                {
                    result.complete( future.getNow() );
                }
                else
                {
                    result.completeExceptionally( future.cause() );
                }
            } );
        }
        return result;
    }

    public static  CompletableFuture failedFuture( Throwable error )
    {
        CompletableFuture result = new CompletableFuture<>();
        result.completeExceptionally( error );
        return result;
    }

    public static  V blockingGet( CompletionStage stage )
    {
        return blockingGet( stage, Futures::noOpInterruptHandler );
    }

    public static  V blockingGet( CompletionStage stage, Runnable interruptHandler )
    {
        EventLoopGroupFactory.assertNotInEventLoopThread();

        Future future = stage.toCompletableFuture();
        boolean interrupted = false;
        try
        {
            while ( true )
            {
                try
                {
                    return future.get();
                }
                catch ( InterruptedException e )
                {
                    // this thread was interrupted while waiting
                    // computation denoted by the future might still be running

                    interrupted = true;

                    // run the interrupt handler and ignore if it throws
                    // need to wait for IO thread to actually finish, can't simply re-rethrow
                    safeRun( interruptHandler );
                }
                catch ( ExecutionException e )
                {
                    ErrorUtil.rethrowAsyncException( e );
                }
            }
        }
        finally
        {
            if ( interrupted )
            {
                Thread.currentThread().interrupt();
            }
        }
    }

    public static  T getNow( CompletionStage stage )
    {
        return stage.toCompletableFuture().getNow( null );
    }

    /**
     * Helper method to extract cause of a {@link CompletionException}.
     * 

* When using {@link CompletionStage#whenComplete(BiConsumer)} and {@link CompletionStage#handle(BiFunction)} * propagated exceptions might get wrapped in a {@link CompletionException}. * * @param error the exception to get cause for. * @return cause of the given exception if it is a {@link CompletionException}, given exception otherwise. */ public static Throwable completionExceptionCause( Throwable error ) { if ( error instanceof CompletionException ) { return error.getCause(); } return error; } /** * Helped method to turn given exception into a {@link CompletionException}. * * @param error the exception to convert. * @return given exception wrapped with {@link CompletionException} if it's not one already. */ public static CompletionException asCompletionException( Throwable error ) { if ( error instanceof CompletionException ) { return ((CompletionException) error); } return new CompletionException( error ); } /** * Combine given errors into a single {@link CompletionException} to be rethrown from inside a * {@link CompletionStage} chain. * * @param error1 the first error or {@code null}. * @param error2 the second error or {@code null}. * @return {@code null} if both errors are null, {@link CompletionException} otherwise. */ public static CompletionException combineErrors( Throwable error1, Throwable error2 ) { if ( error1 != null && error2 != null ) { Throwable cause1 = completionExceptionCause( error1 ); Throwable cause2 = completionExceptionCause( error2 ); addSuppressed( cause1, cause2 ); return asCompletionException( cause1 ); } else if ( error1 != null ) { return asCompletionException( error1 ); } else if ( error2 != null ) { return asCompletionException( error2 ); } else { return null; } } /** * Given a future, if the future completes successfully then return a new completed future with the completed value. * Otherwise if the future completes with an error, then this method first saves the error in the error recorder, and then continues with the onErrorAction. * @param future the future. * @param errorRecorder saves error if the given future completes with an error. * @param onErrorAction continues the future with this action if the future completes with an error. * @param type * @return a new completed future with the same completed value if the given future completes successfully, otherwise continues with the onErrorAction. */ @SuppressWarnings( "ThrowableNotThrown" ) public static CompletableFuture onErrorContinue( CompletableFuture future, Throwable errorRecorder, Function> onErrorAction ) { Objects.requireNonNull( future ); return future.handle( ( value, error ) -> { if ( error != null ) { // record error Futures.combineErrors( errorRecorder, error ); return new CompletionResult( null, error ); } return new CompletionResult<>( value, null ); } ).thenCompose( result -> { if ( result.value != null ) { return completedFuture( result.value ); } else { return onErrorAction.apply( result.error ); } } ); } private static class CompletionResult { T value; Throwable error; CompletionResult( T value, Throwable error ) { this.value = value; this.error = error; } } private static void safeRun( Runnable runnable ) { try { runnable.run(); } catch ( Throwable ignore ) { } } private static void noOpInterruptHandler() { } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy