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

org.neo4j.driver.internal.async.InternalAsyncSession 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.async;

import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

import org.neo4j.driver.AccessMode;
import org.neo4j.driver.Bookmark;
import org.neo4j.driver.Query;
import org.neo4j.driver.TransactionConfig;
import org.neo4j.driver.async.AsyncSession;
import org.neo4j.driver.async.AsyncTransaction;
import org.neo4j.driver.async.AsyncTransactionWork;
import org.neo4j.driver.async.ResultCursor;
import org.neo4j.driver.internal.util.Futures;

import static java.util.Collections.emptyMap;
import static org.neo4j.driver.internal.util.Futures.completedWithNull;
import static org.neo4j.driver.internal.util.Futures.failedFuture;

public class InternalAsyncSession extends AsyncAbstractQueryRunner implements AsyncSession
{
    private final NetworkSession session;

    public InternalAsyncSession( NetworkSession session )
    {
        this.session = session;
    }

    @Override
    public CompletionStage runAsync(Query query)
    {
        return runAsync(query, TransactionConfig.empty() );
    }

    @Override
    public CompletionStage runAsync(String query, TransactionConfig config )
    {
        return runAsync(query, emptyMap(), config );
    }

    @Override
    public CompletionStage runAsync(String query, Map parameters, TransactionConfig config )
    {
        return runAsync( new Query(query, parameters ), config );
    }

    @Override
    public CompletionStage runAsync(Query query, TransactionConfig config )
    {
        return session.runAsync( query, config );
    }

    @Override
    public CompletionStage closeAsync()
    {
        return session.closeAsync();
    }

    @Override
    public CompletionStage beginTransactionAsync()
    {
        return beginTransactionAsync( TransactionConfig.empty() );
    }

    @Override
    public CompletionStage beginTransactionAsync( TransactionConfig config )
    {
        return session.beginTransactionAsync( config ).thenApply( InternalAsyncTransaction::new );
    }

    @Override
    public  CompletionStage readTransactionAsync( AsyncTransactionWork> work )
    {
        return readTransactionAsync( work, TransactionConfig.empty() );
    }

    @Override
    public  CompletionStage readTransactionAsync( AsyncTransactionWork> work, TransactionConfig config )
    {
        return transactionAsync( AccessMode.READ, work, config );
    }

    @Override
    public  CompletionStage writeTransactionAsync( AsyncTransactionWork> work )
    {
        return writeTransactionAsync( work, TransactionConfig.empty() );
    }

    @Override
    public  CompletionStage writeTransactionAsync( AsyncTransactionWork> work, TransactionConfig config )
    {
        return transactionAsync( AccessMode.WRITE, work, config );
    }

    @Override
    public Bookmark lastBookmark()
    {
        return session.lastBookmark();
    }

    private  CompletionStage transactionAsync( AccessMode mode, AsyncTransactionWork> work, TransactionConfig config )
    {
        return session.retryLogic().retryAsync( () -> {
            CompletableFuture resultFuture = new CompletableFuture<>();
            CompletionStage txFuture = session.beginTransactionAsync( mode, config );

            txFuture.whenComplete( ( tx, completionError ) -> {
                Throwable error = Futures.completionExceptionCause( completionError );
                if ( error != null )
                {
                    resultFuture.completeExceptionally( error );
                }
                else
                {
                    executeWork( resultFuture, tx, work );
                }
            } );

            return resultFuture;
        } );
    }

    private  void executeWork(CompletableFuture resultFuture, UnmanagedTransaction tx, AsyncTransactionWork> work )
    {
        CompletionStage workFuture = safeExecuteWork( tx, work );
        workFuture.whenComplete( ( result, completionError ) -> {
            Throwable error = Futures.completionExceptionCause( completionError );
            if ( error != null )
            {
                closeTxAfterFailedTransactionWork( tx, resultFuture, error );
            }
            else
            {
                closeTxAfterSucceededTransactionWork( tx, resultFuture, result );
            }
        } );
    }

    private  CompletionStage safeExecuteWork(UnmanagedTransaction tx, AsyncTransactionWork> work )
    {
        // given work might fail in both async and sync way
        // async failure will result in a failed future being returned
        // sync failure will result in an exception being thrown
        try
        {
            CompletionStage result = work.execute( new InternalAsyncTransaction( tx ) );

            // protect from given transaction function returning null
            return result == null ? completedWithNull() : result;
        }
        catch ( Throwable workError )
        {
            // work threw an exception, wrap it in a future and proceed
            return failedFuture( workError );
        }
    }

    private  void closeTxAfterFailedTransactionWork( UnmanagedTransaction tx, CompletableFuture resultFuture, Throwable error )
    {
        tx.closeAsync().whenComplete(
                ( ignored, rollbackError ) ->
                {
                    if ( rollbackError != null )
                    {
                        error.addSuppressed( rollbackError );
                    }
                    resultFuture.completeExceptionally( error );
                } );
    }

    private  void closeTxAfterSucceededTransactionWork(UnmanagedTransaction tx, CompletableFuture resultFuture, T result )
    {
        tx.closeAsync( true ).whenComplete(
                ( ignored, completionError ) ->
                {
                    Throwable commitError = Futures.completionExceptionCause( completionError );
                    if ( commitError != null )
                    {
                        resultFuture.completeExceptionally( commitError );
                    }
                    else
                    {
                        resultFuture.complete( result );
                    }
                } );
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy