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

com.ing.data.cassandra.jdbc.PooledCassandraConnection Maven / Gradle / Ivy

There is a newer version: 4.13.1
Show newest version
/*
 *
 *   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 com.ing.data.cassandra.jdbc;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.PooledConnection;
import javax.sql.StatementEvent;
import javax.sql.StatementEventListener;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLRecoverableException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Pooled Cassandra connection: implementation class for {@link PooledConnection} to create a JDBC pooled connection to
 * a Cassandra cluster.
 */
public class PooledCassandraConnection implements PooledConnection {
    private static final Logger LOG = LoggerFactory.getLogger(PooledCassandraConnection.class);

    volatile Set connectionEventListeners = new HashSet<>();
    volatile Set statementEventListeners = new HashSet<>();

    private final CassandraConnection physicalConnection;
    private final Map> freePreparedStatements = new HashMap<>();
    private final Map> usedPreparedStatements = new HashMap<>();

    /**
     * Constructor.
     *
     * @param physicalConnection The physical {@link CassandraConnection}.
     */
    public PooledCassandraConnection(final CassandraConnection physicalConnection) {
        this.physicalConnection = physicalConnection;
    }

    @Override
    public CassandraConnection getConnection() {
        return this.physicalConnection;
    }

    @Override
    public void close() throws SQLException {
        this.physicalConnection.close();
    }

    @Override
    public void addConnectionEventListener(final ConnectionEventListener listener) {
        this.connectionEventListeners.add(listener);
    }

    @Override
    public void removeConnectionEventListener(final ConnectionEventListener listener) {
        this.connectionEventListeners.remove(listener);
    }

    @Override
    public void addStatementEventListener(final StatementEventListener listener) {
        this.statementEventListeners.add(listener);
    }

    @Override
    public void removeStatementEventListener(final StatementEventListener listener) {
        this.statementEventListeners.remove(listener);
    }

    /**
     * Notifies each registered {@link ConnectionEventListener} that the application has called the method
     * close() on its representation of the pooled connection (here a {@link ManagedConnection}).
     *
     * @see ManagedConnection#close()
     */
    void connectionClosed() {
        final ConnectionEvent event = new ConnectionEvent(this);
        for (final ConnectionEventListener listener : this.connectionEventListeners) {
            listener.connectionClosed(event);
        }
    }

    /**
     * Notifies each registered {@link ConnectionEventListener} that a fatal error has occurred and the pooled
     * connection can no longer be used.
     *
     * @param sqlException The SQL exception.
     * @see ManagedConnection
     */
    void connectionErrorOccurred(final SQLException sqlException) {
        final ConnectionEvent event = new ConnectionEvent(this, sqlException);
        for (final ConnectionEventListener listener : this.connectionEventListeners) {
            listener.connectionErrorOccurred(event);
        }
    }

    /**
     * Notifies each registered {@link StatementEventListener} that a prepared statement is closed.
     *
     * @param preparedStatement The prepared statement.
     * @see ManagedPreparedStatement#close()
     */
    void statementClosed(final CassandraPreparedStatement preparedStatement) {
        final StatementEvent event = new StatementEvent(this, preparedStatement);
        for (final StatementEventListener listener : this.statementEventListeners) {
            listener.statementClosed(event);
        }

        final String cql = preparedStatement.getCql();
        final Set freeStatements = this.freePreparedStatements.get(cql);
        final Set usedStatements = this.usedPreparedStatements.get(cql);

        usedStatements.remove(preparedStatement);

        preparedStatement.resetResults();
        try {
            preparedStatement.clearParameters();
            freeStatements.add(preparedStatement);
        } catch (final SQLException e) {
            LOG.error(e.getMessage());
        }
    }

    /**
     * Notifies each registered {@link StatementEventListener} that a prepared statement is invalid.
     *
     * @param preparedStatement The prepared statement.
     * @param sqlException      The SQL exception.
     * @see ManagedPreparedStatement
     */
    void statementErrorOccurred(final CassandraPreparedStatement preparedStatement, final SQLException sqlException) {
        final StatementEvent event = new StatementEvent(this, preparedStatement, sqlException);
        for (final StatementEventListener listener : this.statementEventListeners) {
            listener.statementErrorOccurred(event);
        }

        final String cql = preparedStatement.getCql();
        final Set usedStatements = this.usedPreparedStatements.get(cql);

        if (!(event.getSQLException() instanceof SQLRecoverableException)) {
            preparedStatement.close();
            usedStatements.remove(preparedStatement);
        }
    }

    /**
     * Creates a {@link PreparedStatement} object for sending parameterized SQL statements to the database on a
     * {@link ManagedConnection} representation of the pooled connection.
     *
     * @param managedConnection The representation of the pooled connection.
     * @param cql               The CQL statement.
     * @return The instantiated {@link ManagedPreparedStatement}.
     * @throws SQLException when something went wrong during the creation of the prepared statement.
     */
    public synchronized ManagedPreparedStatement prepareStatement(final ManagedConnection managedConnection,
                                                                  final String cql) throws SQLException {
        if (!this.freePreparedStatements.containsKey(cql)) {
            this.freePreparedStatements.put(cql, new HashSet<>());
            this.usedPreparedStatements.put(cql, new HashSet<>());
        }

        final Set freeStatements = this.freePreparedStatements.get(cql);
        final Set usedStatements = this.usedPreparedStatements.get(cql);

        final CassandraPreparedStatement managedPreparedStatement;
        if (freeStatements.isEmpty()) {
            managedPreparedStatement = this.physicalConnection.prepareStatement(cql);
        } else {
            managedPreparedStatement = freeStatements.iterator().next();
            freeStatements.remove(managedPreparedStatement);
        }
        usedStatements.add(managedPreparedStatement);

        return new ManagedPreparedStatement(this, managedConnection, managedPreparedStatement);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy