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

org.neo4j.driver.internal.InternalTransaction Maven / Gradle / Ivy

There is a newer version: 5.27.0
Show newest version
/**
 * Copyright (c) 2002-2016 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.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;

import java.util.Collections;
import java.util.Map;

import org.neo4j.driver.internal.spi.Connection;
import org.neo4j.driver.internal.types.InternalTypeSystem;
import org.neo4j.driver.v1.ResultCursor;
import org.neo4j.driver.v1.Statement;
import org.neo4j.driver.v1.Transaction;
import org.neo4j.driver.v1.TypeSystem;
import org.neo4j.driver.v1.Value;
import org.neo4j.driver.v1.exceptions.ClientException;
import org.neo4j.driver.v1.exceptions.Neo4jException;

public class InternalTransaction implements Transaction
{
    private final Runnable cleanup;
    private Connection conn;

    private enum State
    {
        /** The transaction is running with no explicit success or failure marked */
        ACTIVE,

        /** Running, user marked for success, meaning it'll value committed */
        MARKED_SUCCESS,

        /** User marked as failed, meaning it'll be rolled back. */
        MARKED_FAILED,

        /**
         * An error has occurred, transaction can no longer be used and no more messages will be sent for this
         * transaction.
         */
        FAILED,

        /** This transaction has successfully committed */
        SUCCEEDED,

        /** This transaction has been rolled back */
        ROLLED_BACK
    }

    private State state = State.ACTIVE;

    public InternalTransaction( Connection conn, Runnable cleanup )
    {
        this.conn = conn;
        this.cleanup = cleanup;

        // Note there is no sync here, so this will just value queued locally
        conn.run( "BEGIN", Collections.emptyMap(), null );
        conn.discardAll();
    }

    @Override
    public void success()
    {
        if ( state == State.ACTIVE )
        {
            state = State.MARKED_SUCCESS;
        }
    }

    @Override
    public void failure()
    {
        if ( state == State.ACTIVE || state == State.MARKED_SUCCESS )
        {
            state = State.MARKED_FAILED;
        }
    }

    @Override
    public void defunct()
    {
        state = State.ROLLED_BACK;
        conn = null;
    }

    @Override
    public void close()
    {
        try
        {
            if ( conn != null && conn.isOpen() )
            {
                if ( state == State.MARKED_SUCCESS )
                {
                    conn.run( "COMMIT", Collections.emptyMap(), null );
                    conn.discardAll();
                    conn.sync();
                    state = State.SUCCEEDED;
                }
                else if ( state == State.MARKED_FAILED || state == State.ACTIVE )
                {
                    // If alwaysValid of the things we've put in the queue have been sent off, there is no need to
                    // do this, we could just clear the queue. Future optimization.
                    conn.run( "ROLLBACK", Collections.emptyMap(), null );
                    conn.discardAll();
                    conn.sync();
                    state = State.ROLLED_BACK;
                }
            }
        }
        finally
        {
            cleanup.run();
        }
    }

    @Override
    @SuppressWarnings( "unchecked" )
    public ResultCursor run( String statementText, Map statementParameters )
    {
        ensureNotFailed();

        try
        {
            InternalResultCursor cursor = new InternalResultCursor( conn, this, statementText, statementParameters );
            conn.run( statementText, statementParameters, cursor.runResponseCollector() );
            conn.pullAll( cursor.pullAllResponseCollector() );
            conn.sendAll();
            return cursor;
        }
        catch ( Neo4jException e )
        {
            state = State.FAILED;
            throw e;
        }
    }

    @Override
    public ResultCursor run( String statementTemplate )
    {
        return run( statementTemplate, ParameterSupport.NO_PARAMETERS );
    }

    @Override
    public ResultCursor run( Statement statement )
    {
        return run( statement.template(), statement.parameters() );
    }

    @Override
    public boolean isOpen()
    {
        return state == State.ACTIVE;
    }

    private void ensureNotFailed()
    {
        if ( state == State.FAILED || state == State.MARKED_FAILED || state == State.ROLLED_BACK )
        {
            throw new ClientException(
                "Cannot run more statements in this transaction, because previous statements in the " +
                "transaction has failed and the transaction has been rolled back. Please start a new" +
                " transaction to run another statement."
            );
        }
    }

    @Override
    public TypeSystem typeSystem()
    {
        return InternalTypeSystem.TYPE_SYSTEM;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy