PooledConnectionImpl.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.dbcp2.cpdsadapter;

  18. import java.sql.CallableStatement;
  19. import java.sql.Connection;
  20. import java.sql.PreparedStatement;
  21. import java.sql.SQLException;
  22. import java.sql.Statement;
  23. import java.util.ArrayList;
  24. import java.util.Collections;
  25. import java.util.List;

  26. import javax.sql.ConnectionEvent;
  27. import javax.sql.ConnectionEventListener;
  28. import javax.sql.PooledConnection;
  29. import javax.sql.StatementEventListener;

  30. import org.apache.commons.dbcp2.DelegatingConnection;
  31. import org.apache.commons.dbcp2.DelegatingPreparedStatement;
  32. import org.apache.commons.dbcp2.Jdbc41Bridge;
  33. import org.apache.commons.dbcp2.PStmtKey;
  34. import org.apache.commons.dbcp2.PoolableCallableStatement;
  35. import org.apache.commons.dbcp2.PoolablePreparedStatement;
  36. import org.apache.commons.dbcp2.PoolingConnection.StatementType;
  37. import org.apache.commons.dbcp2.Utils;
  38. import org.apache.commons.pool2.KeyedObjectPool;
  39. import org.apache.commons.pool2.KeyedPooledObjectFactory;
  40. import org.apache.commons.pool2.PooledObject;
  41. import org.apache.commons.pool2.impl.DefaultPooledObject;

  42. /**
  43.  * Implements {@link PooledConnection} that is returned by {@link DriverAdapterCPDS}.
  44.  *
  45.  * @since 2.0
  46.  */
  47. final class PooledConnectionImpl
  48.         implements PooledConnection, KeyedPooledObjectFactory<PStmtKey, DelegatingPreparedStatement> {

  49.     private static final String CLOSED = "Attempted to use PooledConnection after closed() was called.";

  50.     /**
  51.      * The JDBC database connection that represents the physical db connection.
  52.      */
  53.     private Connection connection;

  54.     /**
  55.      * A DelegatingConnection used to create a PoolablePreparedStatementStub.
  56.      */
  57.     private final DelegatingConnection<?> delegatingConnection;

  58.     /**
  59.      * The JDBC database logical connection.
  60.      */
  61.     private Connection logicalConnection;

  62.     /**
  63.      * ConnectionEventListeners.
  64.      */
  65.     private final List<ConnectionEventListener> eventListeners;

  66.     /**
  67.      * StatementEventListeners.
  68.      */
  69.     private final List<StatementEventListener> statementEventListeners = Collections.synchronizedList(new ArrayList<>());

  70.     /**
  71.      * Flag set to true, once {@link #close()} is called.
  72.      */
  73.     private boolean closed;

  74.     /** My pool of {@link PreparedStatement}s. */
  75.     private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pStmtPool;

  76.     /**
  77.      * Controls access to the underlying connection.
  78.      */
  79.     private boolean accessToUnderlyingConnectionAllowed;

  80.     /**
  81.      * Wraps a real connection.
  82.      *
  83.      * @param connection
  84.      *            the connection to be wrapped.
  85.      */
  86.     PooledConnectionImpl(final Connection connection) {
  87.         this.connection = connection;
  88.         if (connection instanceof DelegatingConnection) {
  89.             this.delegatingConnection = (DelegatingConnection<?>) connection;
  90.         } else {
  91.             this.delegatingConnection = new DelegatingConnection<>(connection);
  92.         }
  93.         eventListeners = Collections.synchronizedList(new ArrayList<>());
  94.         closed = false;
  95.     }

  96.     /**
  97.      * My {@link KeyedPooledObjectFactory} method for activating {@link PreparedStatement}s.
  98.      *
  99.      * @param pooledObject Activates the underlying object.
  100.      */
  101.     @Override
  102.     public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
  103.             throws SQLException {
  104.         pooledObject.getObject().activate();
  105.     }

  106.     /**
  107.      * {@inheritDoc}
  108.      */
  109.     @Override
  110.     public void addConnectionEventListener(final ConnectionEventListener listener) {
  111.         if (!eventListeners.contains(listener)) {
  112.             eventListeners.add(listener);
  113.         }
  114.     }

  115.     @Override
  116.     public void addStatementEventListener(final StatementEventListener listener) {
  117.         if (!statementEventListeners.contains(listener)) {
  118.             statementEventListeners.add(listener);
  119.         }
  120.     }

  121.     /**
  122.      * Throws an SQLException, if isClosed is true
  123.      */
  124.     private void assertOpen() throws SQLException {
  125.         if (closed || connection == null) {
  126.             throw new SQLException(CLOSED);
  127.         }
  128.     }

  129.     /**
  130.      * Closes the physical connection and marks this {@code PooledConnection} so that it may not be used to
  131.      * generate any more logical {@code Connection}s.
  132.      *
  133.      * @throws SQLException
  134.      *             Thrown when an error occurs or the connection is already closed.
  135.      */
  136.     @Override
  137.     public void close() throws SQLException {
  138.         assertOpen();
  139.         closed = true;
  140.         try {
  141.             if (pStmtPool != null) {
  142.                 try {
  143.                     pStmtPool.close();
  144.                 } finally {
  145.                     pStmtPool = null;
  146.                 }
  147.             }
  148.         } catch (final RuntimeException e) {
  149.             throw e;
  150.         } catch (final Exception e) {
  151.             throw new SQLException("Cannot close connection (return to pool failed)", e);
  152.         } finally {
  153.             try {
  154.                 connection.close();
  155.             } finally {
  156.                 connection = null;
  157.             }
  158.         }
  159.     }

  160.     /**
  161.      * Creates a {@link PStmtKey} for the given arguments.
  162.      *
  163.      * @param sql
  164.      *            The SQL statement.
  165.      * @return a {@link PStmtKey} for the given arguments.
  166.      */
  167.     protected PStmtKey createKey(final String sql) {
  168.         return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull());
  169.     }

  170.     /**
  171.      * Creates a {@link PStmtKey} for the given arguments.
  172.      *
  173.      * @param sql
  174.      *            The SQL statement.
  175.      * @param autoGeneratedKeys
  176.      *            A flag indicating whether auto-generated keys should be returned; one of
  177.      *            {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}.
  178.      * @return a key to uniquely identify a prepared statement.
  179.      */
  180.     protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) {
  181.         return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), autoGeneratedKeys);
  182.     }

  183.     /**
  184.      * Creates a {@link PStmtKey} for the given arguments.
  185.      *
  186.      * @param sql
  187.      *            The SQL statement.
  188.      * @param resultSetType
  189.      *            A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
  190.      *            {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
  191.      * @param resultSetConcurrency
  192.      *            A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
  193.      *            {@code ResultSet.CONCUR_UPDATABLE}.
  194.      * @return a key to uniquely identify a prepared statement.
  195.      */
  196.     protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
  197.         return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency);
  198.     }

  199.     /**
  200.      * Creates a {@link PStmtKey} for the given arguments.
  201.      *
  202.      * @param sql
  203.      *            The SQL statement.
  204.      * @param resultSetType
  205.      *            a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
  206.      *            {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
  207.      * @param resultSetConcurrency
  208.      *            A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
  209.      *            {@code ResultSet.CONCUR_UPDATABLE}
  210.      * @param resultSetHoldability
  211.      *            One of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT}
  212.      *            or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}.
  213.      * @return a key to uniquely identify a prepared statement.
  214.      */
  215.     protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) {
  216.         return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, resultSetHoldability);
  217.     }

  218.     /**
  219.      * Creates a {@link PStmtKey} for the given arguments.
  220.      *
  221.      * @param sql
  222.      *            The SQL statement.
  223.      * @param resultSetType
  224.      *            a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
  225.      *            {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}
  226.      * @param resultSetConcurrency
  227.      *            A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
  228.      *            {@code ResultSet.CONCUR_UPDATABLE}.
  229.      * @param resultSetHoldability
  230.      *            One of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT}
  231.      *            or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}.
  232.      * @param statementType
  233.      *            The SQL statement type, prepared or callable.
  234.      * @return a key to uniquely identify a prepared statement.
  235.      * @since 2.4.0
  236.      */
  237.     protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability,
  238.         final StatementType statementType) {
  239.         return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, resultSetHoldability, statementType);
  240.     }

  241.     /**
  242.      * Creates a {@link PStmtKey} for the given arguments.
  243.      *
  244.      * @param sql
  245.      *            The SQL statement.
  246.      * @param resultSetType
  247.      *            A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
  248.      *            {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
  249.      * @param resultSetConcurrency
  250.      *            A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
  251.      *            {@code ResultSet.CONCUR_UPDATABLE}.
  252.      * @param statementType
  253.      *            The SQL statement type, prepared or callable.
  254.      * @return a key to uniquely identify a prepared statement.
  255.      * @since 2.4.0
  256.      */
  257.     protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, final StatementType statementType) {
  258.         return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, statementType);
  259.     }

  260.     /**
  261.      * Creates a {@link PStmtKey} for the given arguments.
  262.      *
  263.      * @param sql
  264.      *            The SQL statement.
  265.      * @param columnIndexes
  266.      *            An array of column indexes indicating the columns that should be returned from the inserted row or
  267.      *            rows.
  268.      * @return a key to uniquely identify a prepared statement.
  269.      */
  270.     protected PStmtKey createKey(final String sql, final int[] columnIndexes) {
  271.         return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), columnIndexes);
  272.     }

  273.     /**
  274.      * Creates a {@link PStmtKey} for the given arguments.
  275.      *
  276.      * @param sql
  277.      *            The SQL statement.
  278.      * @param statementType
  279.      *            The SQL statement type, prepared or callable.
  280.      * @return a key to uniquely identify a prepared statement.
  281.      */
  282.     protected PStmtKey createKey(final String sql, final StatementType statementType) {
  283.         return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), statementType);
  284.     }

  285.     /**
  286.      * Creates a {@link PStmtKey} for the given arguments.
  287.      *
  288.      * @param sql
  289.      *            The SQL statement.
  290.      * @param columnNames
  291.      *            An array of column names indicating the columns that should be returned from the inserted row or rows.
  292.      * @return a key to uniquely identify a prepared statement.
  293.      */
  294.     protected PStmtKey createKey(final String sql, final String[] columnNames) {
  295.         return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), columnNames);
  296.     }

  297.     /**
  298.      * My {@link KeyedPooledObjectFactory} method for destroying {@link PreparedStatement}s.
  299.      *
  300.      * @param key
  301.      *            ignored
  302.      * @param pooledObject
  303.      *            the wrapped {@link PreparedStatement} to be destroyed.
  304.      */
  305.     @Override
  306.     public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) throws SQLException {
  307.         if (pooledObject != null) {
  308.             @SuppressWarnings("resource")
  309.             final DelegatingPreparedStatement object = pooledObject.getObject();
  310.             if (object != null) {
  311.                 @SuppressWarnings("resource")
  312.                 final Statement innermostDelegate = object.getInnermostDelegate();
  313.                 if (innermostDelegate != null) {
  314.                     innermostDelegate.close();
  315.                 }
  316.             }
  317.         }
  318.     }

  319.     /**
  320.      * Closes the physical connection and checks that the logical connection was closed as well.
  321.      */
  322.     @Override
  323.     protected void finalize() throws Throwable {
  324.         // Closing the Connection ensures that if anyone tries to use it,
  325.         // an error will occur.
  326.         Utils.close(connection, null);
  327.         // make sure the last connection is marked as closed
  328.         if (logicalConnection != null && !logicalConnection.isClosed()) {
  329.             throw new SQLException("PooledConnection was gc'ed, without its last Connection being closed.");
  330.         }
  331.     }

  332.     private String getCatalogOrNull() {
  333.         try {
  334.             return connection == null ? null : connection.getCatalog();
  335.         } catch (final SQLException e) {
  336.             return null;
  337.         }
  338.     }

  339.     /**
  340.      * Returns a JDBC connection.
  341.      *
  342.      * @return The database connection.
  343.      * @throws SQLException
  344.      *             if the connection is not open or the previous logical connection is still open
  345.      */
  346.     @Override
  347.     public Connection getConnection() throws SQLException {
  348.         assertOpen();
  349.         // make sure the last connection is marked as closed
  350.         if (logicalConnection != null && !logicalConnection.isClosed()) {
  351.             // should notify pool of error so the pooled connection can
  352.             // be removed !FIXME!
  353.             throw new SQLException("PooledConnection was reused, without its previous Connection being closed.");
  354.         }

  355.         // the spec requires that this return a new Connection instance.
  356.         logicalConnection = new ConnectionImpl(this, connection, isAccessToUnderlyingConnectionAllowed());
  357.         return logicalConnection;
  358.     }

  359.     private Connection getRawConnection() throws SQLException {
  360.         assertOpen();
  361.         return connection;
  362.     }

  363.     private String getSchemaOrNull() {
  364.         try {
  365.             return connection == null ? null : Jdbc41Bridge.getSchema(connection);
  366.         } catch (final SQLException e) {
  367.             return null;
  368.         }
  369.     }

  370.     /**
  371.      * Returns the value of the accessToUnderlyingConnectionAllowed property.
  372.      *
  373.      * @return true if access to the underlying is allowed, false otherwise.
  374.      */
  375.     public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
  376.         return this.accessToUnderlyingConnectionAllowed;
  377.     }

  378.     /**
  379.      * My {@link KeyedPooledObjectFactory} method for creating {@link PreparedStatement}s.
  380.      *
  381.      * @param key
  382.      *            The key for the {@link PreparedStatement} to be created.
  383.      */
  384.     @SuppressWarnings("resource")
  385.     @Override
  386.     public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey key) throws SQLException {
  387.         if (null == key) {
  388.             throw new IllegalArgumentException("Prepared statement key is null or invalid.");
  389.         }
  390.         if (key.getStmtType() == StatementType.PREPARED_STATEMENT) {
  391.             final PreparedStatement statement = (PreparedStatement) key.createStatement(connection);
  392.             @SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this
  393.             final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pStmtPool,
  394.                     delegatingConnection);
  395.             return new DefaultPooledObject<>(pps);
  396.         }
  397.         final CallableStatement statement = (CallableStatement) key.createStatement(connection);
  398.         @SuppressWarnings("unchecked")
  399.         final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pStmtPool,
  400.                 (DelegatingConnection<Connection>) delegatingConnection);
  401.         return new DefaultPooledObject<>(pcs);
  402.     }

  403.     /**
  404.      * Sends a connectionClosed event.
  405.      */
  406.     void notifyListeners() {
  407.         final ConnectionEvent event = new ConnectionEvent(this);
  408.         new ArrayList<>(eventListeners).forEach(listener -> listener.connectionClosed(event));
  409.     }

  410.     /**
  411.      * My {@link KeyedPooledObjectFactory} method for passivating {@link PreparedStatement}s. Currently, invokes
  412.      * {@link PreparedStatement#clearParameters}.
  413.      *
  414.      * @param key
  415.      *            ignored
  416.      * @param pooledObject
  417.      *            a wrapped {@link PreparedStatement}
  418.      */
  419.     @Override
  420.     public void passivateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
  421.             throws SQLException {
  422.         @SuppressWarnings("resource")
  423.         final DelegatingPreparedStatement dps = pooledObject.getObject();
  424.         dps.clearParameters();
  425.         dps.passivate();
  426.     }

  427.     /**
  428.      * Creates or obtains a {@link CallableStatement} from my pool.
  429.      *
  430.      * @param sql
  431.      *            an SQL statement that may contain one or more '?' parameter placeholders. Typically, this statement is
  432.      *            specified using JDBC call escape syntax.
  433.      * @return a default {@code CallableStatement} object containing the pre-compiled SQL statement.
  434.      * @throws SQLException
  435.      *                Thrown if a database access error occurs or this method is called on a closed connection.
  436.      * @since 2.4.0
  437.      */
  438.     @SuppressWarnings("resource") // getRawConnection() does not allocate
  439.     CallableStatement prepareCall(final String sql) throws SQLException {
  440.         if (pStmtPool == null) {
  441.             return getRawConnection().prepareCall(sql);
  442.         }
  443.         try {
  444.             return (CallableStatement) pStmtPool.borrowObject(createKey(sql, StatementType.CALLABLE_STATEMENT));
  445.         } catch (final RuntimeException e) {
  446.             throw e;
  447.         } catch (final Exception e) {
  448.             throw new SQLException("Borrow prepareCall from pool failed", e);
  449.         }
  450.     }

  451.     /**
  452.      * Creates or obtains a {@link CallableStatement} from my pool.
  453.      *
  454.      * @param sql
  455.      *            a {@code String} object that is the SQL statement to be sent to the database; may contain on or
  456.      *            more '?' parameters.
  457.      * @param resultSetType
  458.      *            a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
  459.      *            {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
  460.      * @param resultSetConcurrency
  461.      *            a concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
  462.      *            {@code ResultSet.CONCUR_UPDATABLE}.
  463.      * @return a {@code CallableStatement} object containing the pre-compiled SQL statement that will produce
  464.      *         {@code ResultSet} objects with the given type and concurrency.
  465.      * @throws SQLException
  466.      *             Thrown if a database access error occurs, this method is called on a closed connection or the given
  467.      *             parameters are not {@code ResultSet} constants indicating type and concurrency.
  468.      * @since 2.4.0
  469.      */
  470.     @SuppressWarnings("resource") // getRawConnection() does not allocate
  471.     CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
  472.             throws SQLException {
  473.         if (pStmtPool == null) {
  474.             return getRawConnection().prepareCall(sql, resultSetType, resultSetConcurrency);
  475.         }
  476.         try {
  477.             return (CallableStatement) pStmtPool.borrowObject(
  478.                     createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT));
  479.         } catch (final RuntimeException e) {
  480.             throw e;
  481.         } catch (final Exception e) {
  482.             throw new SQLException("Borrow prepareCall from pool failed", e);
  483.         }
  484.     }

  485.     /**
  486.      * Creates or obtains a {@link CallableStatement} from my pool.
  487.      *
  488.      * @param sql
  489.      *            a {@code String} object that is the SQL statement to be sent to the database; may contain on or
  490.      *            more '?' parameters.
  491.      * @param resultSetType
  492.      *            one of the following {@code ResultSet} constants: {@code ResultSet.TYPE_FORWARD_ONLY},
  493.      *            {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
  494.      * @param resultSetConcurrency
  495.      *            one of the following {@code ResultSet} constants: {@code ResultSet.CONCUR_READ_ONLY} or
  496.      *            {@code ResultSet.CONCUR_UPDATABLE}.
  497.      * @param resultSetHoldability
  498.      *            one of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT}
  499.      *            or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}.
  500.      * @return a new {@code CallableStatement} object, containing the pre-compiled SQL statement, that will
  501.      *         generate {@code ResultSet} objects with the given type, concurrency, and holdability.
  502.      * @throws SQLException
  503.      *             Thrown if a database access error occurs, this method is called on a closed connection or the given
  504.      *             parameters are not {@code ResultSet} constants indicating type, concurrency, and holdability.
  505.      * @since 2.4.0
  506.      */
  507.     @SuppressWarnings("resource") // getRawConnection() does not allocate
  508.     CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
  509.             final int resultSetHoldability) throws SQLException {
  510.         if (pStmtPool == null) {
  511.             return getRawConnection().prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
  512.         }
  513.         try {
  514.             return (CallableStatement) pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency,
  515.                     resultSetHoldability, StatementType.CALLABLE_STATEMENT));
  516.         } catch (final RuntimeException e) {
  517.             throw e;
  518.         } catch (final Exception e) {
  519.             throw new SQLException("Borrow prepareCall from pool failed", e);
  520.         }
  521.     }

  522.     /**
  523.      * Creates or obtains a {@link PreparedStatement} from my pool.
  524.      *
  525.      * @param sql the SQL statement.
  526.      * @return a {@link PoolablePreparedStatement}
  527.      * @throws SQLException Thrown if a database access error occurs, this method is called on a closed connection, or
  528.      *         the borrow failed.
  529.      */
  530.     @SuppressWarnings("resource") // getRawConnection() does not allocate
  531.     PreparedStatement prepareStatement(final String sql) throws SQLException {
  532.         if (pStmtPool == null) {
  533.             return getRawConnection().prepareStatement(sql);
  534.         }
  535.         try {
  536.             return pStmtPool.borrowObject(createKey(sql));
  537.         } catch (final RuntimeException e) {
  538.             throw e;
  539.         } catch (final Exception e) {
  540.             throw new SQLException("Borrow prepareStatement from pool failed", e);
  541.         }
  542.     }

  543.     /**
  544.      * Creates or obtains a {@link PreparedStatement} from my pool.
  545.      *
  546.      * @param sql
  547.      *            an SQL statement that may contain one or more '?' IN parameter placeholders.
  548.      * @param autoGeneratedKeys
  549.      *            a flag indicating whether auto-generated keys should be returned; one of
  550.      *            {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}.
  551.      * @return a {@link PoolablePreparedStatement}
  552.      * @throws SQLException Thrown if a database access error occurs, this method is called on a closed connection, or
  553.      *         the borrow failed.
  554.      * @see Connection#prepareStatement(String, int)
  555.      */
  556.     @SuppressWarnings("resource") // getRawConnection() does not allocate
  557.     PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
  558.         if (pStmtPool == null) {
  559.             return getRawConnection().prepareStatement(sql, autoGeneratedKeys);
  560.         }
  561.         try {
  562.             return pStmtPool.borrowObject(createKey(sql, autoGeneratedKeys));
  563.         } catch (final RuntimeException e) {
  564.             throw e;
  565.         } catch (final Exception e) {
  566.             throw new SQLException("Borrow prepareStatement from pool failed", e);
  567.         }
  568.     }

  569.     /**
  570.      * Creates or obtains a {@link PreparedStatement} from my pool.
  571.      *
  572.      * @param sql
  573.      *            a {@code String} object that is the SQL statement to be sent to the database; may contain one or
  574.      *            more '?' IN parameters.
  575.      * @param resultSetType
  576.      *            a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
  577.      *            {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
  578.      * @param resultSetConcurrency
  579.      *            a concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
  580.      *            {@code ResultSet.CONCUR_UPDATABLE}.
  581.      *
  582.      * @return a {@link PoolablePreparedStatement}.
  583.      * @throws SQLException Thrown if a database access error occurs, this method is called on a closed connection, or
  584.      *         the borrow failed.
  585.      * @see Connection#prepareStatement(String, int, int)
  586.      */
  587.     @SuppressWarnings("resource") // getRawConnection() does not allocate
  588.     PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
  589.             throws SQLException {
  590.         if (pStmtPool == null) {
  591.             return getRawConnection().prepareStatement(sql, resultSetType, resultSetConcurrency);
  592.         }
  593.         try {
  594.             return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency));
  595.         } catch (final RuntimeException e) {
  596.             throw e;
  597.         } catch (final Exception e) {
  598.             throw new SQLException("Borrow prepareStatement from pool failed", e);
  599.         }
  600.     }

  601.     @SuppressWarnings("resource") // getRawConnection() does not allocate
  602.     PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
  603.             final int resultSetHoldability) throws SQLException {
  604.         if (pStmtPool == null) {
  605.             return getRawConnection().prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
  606.         }
  607.         try {
  608.             return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
  609.         } catch (final RuntimeException e) {
  610.             throw e;
  611.         } catch (final Exception e) {
  612.             throw new SQLException("Borrow prepareStatement from pool failed", e);
  613.         }
  614.     }

  615.     @SuppressWarnings("resource") // getRawConnection() does not allocate
  616.     PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
  617.         if (pStmtPool == null) {
  618.             return getRawConnection().prepareStatement(sql, columnIndexes);
  619.         }
  620.         try {
  621.             return pStmtPool.borrowObject(createKey(sql, columnIndexes));
  622.         } catch (final RuntimeException e) {
  623.             throw e;
  624.         } catch (final Exception e) {
  625.             throw new SQLException("Borrow prepareStatement from pool failed", e);
  626.         }
  627.     }

  628.     @SuppressWarnings("resource") // getRawConnection() does not allocate
  629.     PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
  630.         if (pStmtPool == null) {
  631.             return getRawConnection().prepareStatement(sql, columnNames);
  632.         }
  633.         try {
  634.             return pStmtPool.borrowObject(createKey(sql, columnNames));
  635.         } catch (final RuntimeException e) {
  636.             throw e;
  637.         } catch (final Exception e) {
  638.             throw new SQLException("Borrow prepareStatement from pool failed", e);
  639.         }
  640.     }

  641.     /**
  642.      * {@inheritDoc}
  643.      */
  644.     @Override
  645.     public void removeConnectionEventListener(final ConnectionEventListener listener) {
  646.         eventListeners.remove(listener);
  647.     }

  648.     @Override
  649.     public void removeStatementEventListener(final StatementEventListener listener) {
  650.         statementEventListeners.remove(listener);
  651.     }

  652.     /**
  653.      * Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
  654.      * the underlying connection. (Default: false.)
  655.      *
  656.      * @param allow
  657.      *            Access to the underlying connection is granted when true.
  658.      */
  659.     public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
  660.         this.accessToUnderlyingConnectionAllowed = allow;
  661.     }

  662.     public void setStatementPool(final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> statementPool) {
  663.         pStmtPool = statementPool;
  664.     }

  665.     /**
  666.      * @since 2.6.0
  667.      */
  668.     @Override
  669.     public synchronized String toString() {
  670.         final StringBuilder builder = new StringBuilder(super.toString());
  671.         builder.append("[connection=");
  672.         builder.append(connection);
  673.         builder.append(", delegatingConnection=");
  674.         builder.append(delegatingConnection);
  675.         builder.append(", logicalConnection=");
  676.         builder.append(logicalConnection);
  677.         builder.append(", eventListeners=");
  678.         builder.append(eventListeners);
  679.         builder.append(", statementEventListeners=");
  680.         builder.append(statementEventListeners);
  681.         builder.append(", closed=");
  682.         builder.append(closed);
  683.         builder.append(", pStmtPool=");
  684.         builder.append(pStmtPool);
  685.         builder.append(", accessToUnderlyingConnectionAllowed=");
  686.         builder.append(accessToUnderlyingConnectionAllowed);
  687.         builder.append("]");
  688.         return builder.toString();
  689.     }

  690.     /**
  691.      * My {@link KeyedPooledObjectFactory} method for validating {@link PreparedStatement}s.
  692.      *
  693.      * @param key
  694.      *            Ignored.
  695.      * @param pooledObject
  696.      *            Ignored.
  697.      * @return {@code true}
  698.      */
  699.     @Override
  700.     public boolean validateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) {
  701.         return true;
  702.     }
  703. }