Modified: tomcat/tc8.0.x/trunk/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PooledConnectionImpl.java URL: http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PooledConnectionImpl.java?rev=1833816&r1=1833815&r2=1833816&view=diff ============================================================================== --- tomcat/tc8.0.x/trunk/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PooledConnectionImpl.java (original) +++ tomcat/tc8.0.x/trunk/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PooledConnectionImpl.java Tue Jun 19 11:21:13 2018 @@ -17,6 +17,7 @@ package org.apache.tomcat.dbcp.dbcp2.cpdsadapter; +import java.sql.CallableStatement; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -28,67 +29,69 @@ import javax.sql.PooledConnection; import javax.sql.StatementEventListener; import org.apache.tomcat.dbcp.dbcp2.DelegatingConnection; +import org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement; +import org.apache.tomcat.dbcp.dbcp2.PStmtKey; +import org.apache.tomcat.dbcp.dbcp2.PoolableCallableStatement; import org.apache.tomcat.dbcp.dbcp2.PoolablePreparedStatement; +import org.apache.tomcat.dbcp.dbcp2.PoolingConnection.StatementType; import org.apache.tomcat.dbcp.pool2.KeyedObjectPool; import org.apache.tomcat.dbcp.pool2.KeyedPooledObjectFactory; import org.apache.tomcat.dbcp.pool2.PooledObject; import org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject; /** - * Implementation of PooledConnection that is returned by - * PooledConnectionDataSource. + * Implementation of PooledConnection that is returned by PooledConnectionDataSource. * - * @author John D. McNally * @since 2.0 */ class PooledConnectionImpl - implements PooledConnection, KeyedPooledObjectFactory<PStmtKeyCPDS, PoolablePreparedStatement<PStmtKeyCPDS>> { + implements PooledConnection, KeyedPooledObjectFactory<PStmtKey, DelegatingPreparedStatement> { - private static final String CLOSED - = "Attempted to use PooledConnection after closed() was called."; + private static final String CLOSED = "Attempted to use PooledConnection after closed() was called."; /** * The JDBC database connection that represents the physical db connection. */ - private Connection connection = null; + private Connection connection; /** - * A DelegatingConnection used to create a PoolablePreparedStatementStub + * A DelegatingConnection used to create a PoolablePreparedStatementStub. */ private final DelegatingConnection<?> delegatingConnection; /** * The JDBC database logical connection. */ - private Connection logicalConnection = null; + private Connection logicalConnection; /** - * ConnectionEventListeners + * ConnectionEventListeners. */ private final Vector<ConnectionEventListener> eventListeners; /** - * StatementEventListeners + * StatementEventListeners. */ - private final Vector<StatementEventListener> statementEventListeners = - new Vector<>(); + private final Vector<StatementEventListener> statementEventListeners = new Vector<>(); /** - * flag set to true, once close() is called. + * Flag set to true, once {@link #close()} is called. */ - private boolean isClosed; + private boolean closed; /** My pool of {@link PreparedStatement}s. */ - private KeyedObjectPool<PStmtKeyCPDS, PoolablePreparedStatement<PStmtKeyCPDS>> pstmtPool = null; + private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pStmtPool; /** - * Controls access to the underlying connection + * Controls access to the underlying connection. */ - private boolean accessToUnderlyingConnectionAllowed = false; + private boolean accessToUnderlyingConnectionAllowed; /** - * Wrap the real connection. - * @param connection the connection to be wrapped + * Wraps the real connection. + * + * @param connection + * the connection to be wrapped. */ PooledConnectionImpl(final Connection connection) { this.connection = connection; @@ -98,12 +101,21 @@ class PooledConnectionImpl this.delegatingConnection = new DelegatingConnection<>(connection); } eventListeners = new Vector<>(); - isClosed = false; + closed = false; } - public void setStatementPool( - final KeyedObjectPool<PStmtKeyCPDS, PoolablePreparedStatement<PStmtKeyCPDS>> statementPool) { - pstmtPool = statementPool; + /** + * My {@link KeyedPooledObjectFactory} method for activating {@link PreparedStatement}s. + * + * @param key + * Ignored. + * @param pooledObject + * Ignored. + */ + @Override + public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) + throws Exception { + pooledObject.getObject().activate(); } /** @@ -126,22 +138,31 @@ class PooledConnectionImpl /* JDBC_4_ANT_KEY_END */ /** - * Closes the physical connection and marks this - * <code>PooledConnection</code> so that it may not be used - * to generate any more logical <code>Connection</code>s. + * Throws an SQLException, if isClosed is true + */ + private void assertOpen() throws SQLException { + if (closed) { + throw new SQLException(CLOSED); + } + } + + /** + * Closes the physical connection and marks this <code>PooledConnection</code> so that it may not be used to + * generate any more logical <code>Connection</code>s. * - * @throws SQLException if an error occurs or the connection is already closed + * @throws SQLException + * Thrown when an error occurs or the connection is already closed. */ @Override public void close() throws SQLException { assertOpen(); - isClosed = true; + closed = true; try { - if (pstmtPool != null) { + if (pStmtPool != null) { try { - pstmtPool.close(); + pStmtPool.close(); } finally { - pstmtPool = null; + pStmtPool = null; } } } catch (final RuntimeException e) { @@ -158,11 +179,115 @@ class PooledConnectionImpl } /** - * Throws an SQLException, if isClosed is true + * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments. */ - private void assertOpen() throws SQLException { - if (isClosed) { - throw new SQLException(CLOSED); + protected PStmtKey createKey(final String sql) { + return new PStmtKey(normalizeSQL(sql), getCatalogOrNull()); + } + + /** + * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments. + */ + protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) { + return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), autoGeneratedKeys); + } + + /** + * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments. + */ + protected PStmtKey createKey(final String sql, final int columnIndexes[]) { + return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), columnIndexes); + } + + /** + * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments. + */ + protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) { + return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), resultSetType, resultSetConcurrency); + } + + /** + * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments. + */ + protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, + final int resultSetHoldability) { + return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), resultSetType, resultSetConcurrency, + resultSetHoldability); + } + + /** + * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments. + * + * @since 2.4.0 + */ + protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, + final int resultSetHoldability, final StatementType statementType) { + return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), resultSetType, resultSetConcurrency, + resultSetHoldability, statementType); + } + + /** + * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments. + * + * @since 2.4.0 + */ + protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, + final StatementType statementType) { + return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), resultSetType, resultSetConcurrency, statementType); + } + + /** + * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments. + */ + protected PStmtKey createKey(final String sql, final StatementType statementType) { + return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), statementType); + } + + /** + * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments. + */ + protected PStmtKey createKey(final String sql, final String columnNames[]) { + return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), columnNames); + } + + /** + * My {@link KeyedPooledObjectFactory} method for destroying {@link PreparedStatement}s. + * + * @param key + * ignored + * @param pooledObject + * the wrapped {@link PreparedStatement} to be destroyed. + */ + @Override + public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) + throws Exception { + pooledObject.getObject().getInnermostDelegate().close(); + } + + /** + * Closes the physical connection and checks that the logical connection was closed as well. + */ + @Override + protected void finalize() throws Throwable { + // Closing the Connection ensures that if anyone tries to use it, + // an error will occur. + try { + connection.close(); + } catch (final Exception ignored) { + // ignore + } + + // make sure the last connection is marked as closed + if (logicalConnection != null && !logicalConnection.isClosed()) { + throw new SQLException("PooledConnection was gc'ed, without its last Connection being closed."); + } + } + + private String getCatalogOrNull() { + try { + return connection == null ? null : connection.getCatalog(); + } catch (final SQLException e) { + return null; } } @@ -170,7 +295,8 @@ class PooledConnectionImpl * Returns a JDBC connection. * * @return The database connection. - * @throws SQLException if the connection is not open or the previous logical connection is still open + * @throws SQLException + * if the connection is not open or the previous logical connection is still open */ @Override public Connection getConnection() throws SQLException { @@ -179,54 +305,58 @@ class PooledConnectionImpl if (logicalConnection != null && !logicalConnection.isClosed()) { // should notify pool of error so the pooled connection can // be removed !FIXME! - throw new SQLException("PooledConnection was reused, without " - + "its previous Connection being closed."); + throw new SQLException("PooledConnection was reused, without its previous Connection being closed."); } // the spec requires that this return a new Connection instance. - logicalConnection = new ConnectionImpl( - this, connection, isAccessToUnderlyingConnectionAllowed()); + logicalConnection = new ConnectionImpl(this, connection, isAccessToUnderlyingConnectionAllowed()); return logicalConnection; } /** - * {@inheritDoc} + * Returns the value of the accessToUnderlyingConnectionAllowed property. + * + * @return true if access to the underlying is allowed, false otherwise. */ - @Override - public void removeConnectionEventListener( - final ConnectionEventListener listener) { - eventListeners.remove(listener); - } - - /* JDBC_4_ANT_KEY_BEGIN */ - @Override - public void removeStatementEventListener(final StatementEventListener listener) { - statementEventListeners.remove(listener); + public synchronized boolean isAccessToUnderlyingConnectionAllowed() { + return this.accessToUnderlyingConnectionAllowed; } - /* JDBC_4_ANT_KEY_END */ /** - * Closes the physical connection and checks that the logical connection - * was closed as well. + * My {@link KeyedPooledObjectFactory} method for creating {@link PreparedStatement}s. + * + * @param key + * The key for the {@link PreparedStatement} to be created. */ + @SuppressWarnings("resource") @Override - protected void finalize() throws Throwable { - // Closing the Connection ensures that if anyone tries to use it, - // an error will occur. - try { - connection.close(); - } catch (final Exception ignored) { + public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey key) throws Exception { + if (null == key) { + throw new IllegalArgumentException("Prepared statement key is null or invalid."); } - - // make sure the last connection is marked as closed - if (logicalConnection != null && !logicalConnection.isClosed()) { - throw new SQLException("PooledConnection was gc'ed, without" - + "its last Connection being closed."); + if (key.getStmtType() == StatementType.PREPARED_STATEMENT) { + final PreparedStatement statement = (PreparedStatement) key.createStatement(connection); + @SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this + final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pStmtPool, + delegatingConnection); + return new DefaultPooledObject<DelegatingPreparedStatement>(pps); } + final CallableStatement statement = (CallableStatement) key.createStatement(connection); + @SuppressWarnings("unchecked") + final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pStmtPool, + (DelegatingConnection<Connection>) delegatingConnection); + return new DefaultPooledObject<DelegatingPreparedStatement>(pcs); } /** - * sends a connectionClosed event. + * Normalizes the given SQL statement, producing a canonical form that is semantically equivalent to the original. + */ + protected String normalizeSQL(final String sql) { + return sql.trim(); + } + + /** + * Sends a connectionClosed event. */ void notifyListeners() { final ConnectionEvent event = new ConnectionEvent(this); @@ -236,52 +366,132 @@ class PooledConnectionImpl } } - // ------------------------------------------------------------------- - // The following code implements a PreparedStatement pool + /** + * My {@link KeyedPooledObjectFactory} method for passivating {@link PreparedStatement}s. Currently invokes + * {@link PreparedStatement#clearParameters}. + * + * @param key + * ignored + * @param pooledObject + * a wrapped {@link PreparedStatement} + */ + @Override + public void passivateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) + throws Exception { + @SuppressWarnings("resource") + final DelegatingPreparedStatement dps = pooledObject.getObject(); + dps.clearParameters(); + dps.passivate(); + } /** - * Create or obtain a {@link PreparedStatement} from my pool. - * @param sql the SQL statement - * @return a {@link PoolablePreparedStatement} + * Creates or obtains a {@link CallableStatement} from my pool. + * + * @param sql + * an SQL statement that may contain one or more '?' parameter placeholders. Typically this statement is + * specified using JDBC call escape syntax. + * @return a default <code>CallableStatement</code> object containing the pre-compiled SQL statement. + * @exception SQLException + * Thrown if a database access error occurs or this method is called on a closed connection. + * @since 2.4.0 + */ + CallableStatement prepareCall(final String sql) throws SQLException { + if (pStmtPool == null) { + return connection.prepareCall(sql); + } + try { + return (CallableStatement) pStmtPool.borrowObject(createKey(sql, StatementType.CALLABLE_STATEMENT)); + } catch (final RuntimeException e) { + throw e; + } catch (final Exception e) { + throw new SQLException("Borrow prepareCall from pool failed", e); + } + } + + /** + * Creates or obtains a {@link CallableStatement} from my pool. + * + * @param sql + * a <code>String</code> object that is the SQL statement to be sent to the database; may contain on or + * more '?' parameters. + * @param resultSetType + * a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>, + * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>. + * @param resultSetConcurrency + * a concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or + * <code>ResultSet.CONCUR_UPDATABLE</code>. + * @return a <code>CallableStatement</code> object containing the pre-compiled SQL statement that will produce + * <code>ResultSet</code> objects with the given type and concurrency. + * @throws SQLException + * Thrown if a database access error occurs, this method is called on a closed connection or the given + * parameters are not <code>ResultSet</code> constants indicating type and concurrency. + * @since 2.4.0 */ - PreparedStatement prepareStatement(final String sql) throws SQLException { - if (pstmtPool == null) { - return connection.prepareStatement(sql); + CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency) + throws SQLException { + if (pStmtPool == null) { + return connection.prepareCall(sql, resultSetType, resultSetConcurrency); } try { - return pstmtPool.borrowObject(createKey(sql)); + return (CallableStatement) pStmtPool.borrowObject( + createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT)); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { - throw new SQLException("Borrow prepareStatement from pool failed", e); + throw new SQLException("Borrow prepareCall from pool failed", e); } } /** - * Create or obtain a {@link PreparedStatement} from my pool. - * @param sql a <code>String</code> object that is the SQL statement to - * be sent to the database; may contain one or more '?' IN - * parameters - * @param resultSetType a result set type; one of - * <code>ResultSet.TYPE_FORWARD_ONLY</code>, - * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or - * <code>ResultSet.TYPE_SCROLL_SENSITIVE</code> - * @param resultSetConcurrency a concurrency type; one of - * <code>ResultSet.CONCUR_READ_ONLY</code> or - * <code>ResultSet.CONCUR_UPDATABLE</code> + * Creates or obtains a {@link CallableStatement} from my pool. * + * @param sql + * a <code>String</code> object that is the SQL statement to be sent to the database; may contain on or + * more '?' parameters. + * @param resultSetType + * one of the following <code>ResultSet</code> constants: <code>ResultSet.TYPE_FORWARD_ONLY</code>, + * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>. + * @param resultSetConcurrency + * one of the following <code>ResultSet</code> constants: <code>ResultSet.CONCUR_READ_ONLY</code> or + * <code>ResultSet.CONCUR_UPDATABLE</code>. + * @param resultSetHoldability + * one of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> + * or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>. + * @return a new <code>CallableStatement</code> object, containing the pre-compiled SQL statement, that will + * generate <code>ResultSet</code> objects with the given type, concurrency, and holdability. + * @throws SQLException + * Thrown if a database access error occurs, this method is called on a closed connection or the given + * parameters are not <code>ResultSet</code> constants indicating type, concurrency, and holdability. + * @since 2.4.0 + */ + CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency, + final int resultSetHoldability) throws SQLException { + if (pStmtPool == null) { + return connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + try { + return (CallableStatement) pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, + resultSetHoldability, StatementType.CALLABLE_STATEMENT)); + } catch (final RuntimeException e) { + throw e; + } catch (final Exception e) { + throw new SQLException("Borrow prepareCall from pool failed", e); + } + } + + /** + * Creates or obtains a {@link PreparedStatement} from my pool. + * + * @param sql + * the SQL statement. * @return a {@link PoolablePreparedStatement} - * @see Connection#prepareStatement(String, int, int) */ - PreparedStatement prepareStatement(final String sql, final int resultSetType, - final int resultSetConcurrency) - throws SQLException { - if (pstmtPool == null) { - return connection.prepareStatement(sql, resultSetType, resultSetConcurrency); + PreparedStatement prepareStatement(final String sql) throws SQLException { + if (pStmtPool == null) { + return connection.prepareStatement(sql); } try { - return pstmtPool.borrowObject( - createKey(sql,resultSetType,resultSetConcurrency)); + return pStmtPool.borrowObject(createKey(sql)); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { @@ -290,23 +500,22 @@ class PooledConnectionImpl } /** - * Create or obtain a {@link PreparedStatement} from my pool. - * @param sql an SQL statement that may contain one or more '?' IN - * parameter placeholders - * @param autoGeneratedKeys a flag indicating whether auto-generated keys - * should be returned; one of - * <code>Statement.RETURN_GENERATED_KEYS</code> or - * <code>Statement.NO_GENERATED_KEYS</code> + * Creates or obtains a {@link PreparedStatement} from my pool. + * + * @param sql + * an SQL statement that may contain one or more '?' IN parameter placeholders. + * @param autoGeneratedKeys + * a flag indicating whether auto-generated keys should be returned; one of + * <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>. * @return a {@link PoolablePreparedStatement} * @see Connection#prepareStatement(String, int) */ - PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) - throws SQLException { - if (pstmtPool == null) { + PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException { + if (pStmtPool == null) { return connection.prepareStatement(sql, autoGeneratedKeys); } try { - return pstmtPool.borrowObject(createKey(sql,autoGeneratedKeys)); + return pStmtPool.borrowObject(createKey(sql, autoGeneratedKeys)); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { @@ -314,16 +523,12 @@ class PooledConnectionImpl } } - PreparedStatement prepareStatement(final String sql, final int resultSetType, - final int resultSetConcurrency, final int resultSetHoldability) - throws SQLException { - if (pstmtPool == null) { - return connection.prepareStatement(sql, resultSetType, - resultSetConcurrency, resultSetHoldability); + PreparedStatement prepareStatement(final String sql, final int columnIndexes[]) throws SQLException { + if (pStmtPool == null) { + return connection.prepareStatement(sql, columnIndexes); } try { - return pstmtPool.borrowObject(createKey(sql, resultSetType, - resultSetConcurrency, resultSetHoldability)); + return pStmtPool.borrowObject(createKey(sql, columnIndexes)); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { @@ -331,13 +536,29 @@ class PooledConnectionImpl } } - PreparedStatement prepareStatement(final String sql, final int columnIndexes[]) - throws SQLException { - if (pstmtPool == null) { - return connection.prepareStatement(sql, columnIndexes); + /** + * Creates or obtains a {@link PreparedStatement} from my pool. + * + * @param sql + * a <code>String</code> object that is the SQL statement to be sent to the database; may contain one or + * more '?' IN parameters. + * @param resultSetType + * a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>, + * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>. + * @param resultSetConcurrency + * a concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or + * <code>ResultSet.CONCUR_UPDATABLE</code>. + * + * @return a {@link PoolablePreparedStatement}. + * @see Connection#prepareStatement(String, int, int) + */ + PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency) + throws SQLException { + if (pStmtPool == null) { + return connection.prepareStatement(sql, resultSetType, resultSetConcurrency); } try { - return pstmtPool.borrowObject(createKey(sql, columnIndexes)); + return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency)); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { @@ -345,13 +566,13 @@ class PooledConnectionImpl } } - PreparedStatement prepareStatement(final String sql, final String columnNames[]) - throws SQLException { - if (pstmtPool == null) { - return connection.prepareStatement(sql, columnNames); + PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency, + final int resultSetHoldability) throws SQLException { + if (pStmtPool == null) { + return connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); } try { - return pstmtPool.borrowObject(createKey(sql, columnNames)); + return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { @@ -359,161 +580,60 @@ class PooledConnectionImpl } } - /** - * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments. - */ - protected PStmtKeyCPDS createKey(final String sql, final int autoGeneratedKeys) { - return new PStmtKeyCPDS(normalizeSQL(sql), autoGeneratedKeys); - } - - /** - * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments. - */ - protected PStmtKeyCPDS createKey(final String sql, final int resultSetType, - final int resultSetConcurrency, final int resultSetHoldability) { - return new PStmtKeyCPDS(normalizeSQL(sql), resultSetType, - resultSetConcurrency, resultSetHoldability); - } - - /** - * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments. - */ - protected PStmtKeyCPDS createKey(final String sql, final int columnIndexes[]) { - return new PStmtKeyCPDS(normalizeSQL(sql), columnIndexes); - } - - /** - * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments. - */ - protected PStmtKeyCPDS createKey(final String sql, final String columnNames[]) { - return new PStmtKeyCPDS(normalizeSQL(sql), columnNames); - } - - /** - * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments. - */ - protected PStmtKeyCPDS createKey(final String sql, final int resultSetType, - final int resultSetConcurrency) { - return new PStmtKeyCPDS(normalizeSQL(sql), resultSetType, - resultSetConcurrency); - } - - /** - * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments. - */ - protected PStmtKeyCPDS createKey(final String sql) { - return new PStmtKeyCPDS(normalizeSQL(sql)); - } - - /** - * Normalize the given SQL statement, producing a - * canonical form that is semantically equivalent to the original. - */ - protected String normalizeSQL(final String sql) { - return sql.trim(); - } - - /** - * My {@link KeyedPooledObjectFactory} method for creating - * {@link PreparedStatement}s. - * @param key the key for the {@link PreparedStatement} to be created - */ - @Override - public PooledObject<PoolablePreparedStatement<PStmtKeyCPDS>> makeObject(final PStmtKeyCPDS key) throws Exception { - if (null == key) { - throw new IllegalArgumentException(); + PreparedStatement prepareStatement(final String sql, final String columnNames[]) throws SQLException { + if (pStmtPool == null) { + return connection.prepareStatement(sql, columnNames); } - // _openPstmts++; - if (null == key.getResultSetType() - && null == key.getResultSetConcurrency()) { - if (null == key.getAutoGeneratedKeys()) { - return new DefaultPooledObject<>(new PoolablePreparedStatement<>( - connection.prepareStatement(key.getSql()), - key, pstmtPool, delegatingConnection)); - } - return new DefaultPooledObject<>(new PoolablePreparedStatement<>( - connection.prepareStatement(key.getSql(), - key.getAutoGeneratedKeys().intValue()), - key, pstmtPool, delegatingConnection)); + try { + return pStmtPool.borrowObject(createKey(sql, columnNames)); + } catch (final RuntimeException e) { + throw e; + } catch (final Exception e) { + throw new SQLException("Borrow prepareStatement from pool failed", e); } - return new DefaultPooledObject<>(new PoolablePreparedStatement<>( - connection.prepareStatement(key.getSql(), - key.getResultSetType().intValue(), - key.getResultSetConcurrency().intValue()), - key, pstmtPool, delegatingConnection)); - } - - /** - * My {@link KeyedPooledObjectFactory} method for destroying - * {@link PreparedStatement}s. - * @param key ignored - * @param p the wrapped {@link PreparedStatement} to be destroyed. - */ - @Override - public void destroyObject(final PStmtKeyCPDS key, - final PooledObject<PoolablePreparedStatement<PStmtKeyCPDS>> p) - throws Exception { - p.getObject().getInnermostDelegate().close(); } /** - * My {@link KeyedPooledObjectFactory} method for validating - * {@link PreparedStatement}s. - * @param key ignored - * @param p ignored - * @return {@code true} + * {@inheritDoc} */ @Override - public boolean validateObject(final PStmtKeyCPDS key, - final PooledObject<PoolablePreparedStatement<PStmtKeyCPDS>> p) { - return true; + public void removeConnectionEventListener(final ConnectionEventListener listener) { + eventListeners.remove(listener); } - /** - * My {@link KeyedPooledObjectFactory} method for activating - * {@link PreparedStatement}s. - * @param key ignored - * @param p ignored - */ + /* JDBC_4_ANT_KEY_BEGIN */ @Override - public void activateObject(final PStmtKeyCPDS key, - final PooledObject<PoolablePreparedStatement<PStmtKeyCPDS>> p) - throws Exception { - p.getObject().activate(); + public void removeStatementEventListener(final StatementEventListener listener) { + statementEventListeners.remove(listener); } + /* JDBC_4_ANT_KEY_END */ /** - * My {@link KeyedPooledObjectFactory} method for passivating - * {@link PreparedStatement}s. Currently invokes {@link PreparedStatement#clearParameters}. - * @param key ignored - * @param p a wrapped {@link PreparedStatement} + * Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to + * the underlying connection. (Default: false.) + * + * @param allow + * Access to the underlying connection is granted when true. */ - @Override - public void passivateObject(final PStmtKeyCPDS key, - final PooledObject<PoolablePreparedStatement<PStmtKeyCPDS>> p) - throws Exception { - final PoolablePreparedStatement<PStmtKeyCPDS> ppss = p.getObject(); - ppss.clearParameters(); - ppss.passivate(); + public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) { + this.accessToUnderlyingConnectionAllowed = allow; } - /** - * Returns the value of the accessToUnderlyingConnectionAllowed property. - * - * @return true if access to the underlying is allowed, false otherwise. - */ - public synchronized boolean isAccessToUnderlyingConnectionAllowed() { - return this.accessToUnderlyingConnectionAllowed; + public void setStatementPool(final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> statementPool) { + pStmtPool = statementPool; } /** - * Sets the value of the accessToUnderlyingConnectionAllowed property. - * It controls if the PoolGuard allows access to the underlying connection. - * (Default: false) + * My {@link KeyedPooledObjectFactory} method for validating {@link PreparedStatement}s. * - * @param allow Access to the underlying connection is granted when true. + * @param key + * Ignored. + * @param pooledObject + * Ignored. + * @return {@code true} */ - public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) { - this.accessToUnderlyingConnectionAllowed = allow; + @Override + public boolean validateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) { + return true; } }
Modified: tomcat/tc8.0.x/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java URL: http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java?rev=1833816&r1=1833815&r2=1833816&view=diff ============================================================================== --- tomcat/tc8.0.x/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java (original) +++ tomcat/tc8.0.x/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java Tue Jun 19 11:21:13 2018 @@ -40,67 +40,86 @@ import org.apache.tomcat.dbcp.pool2.impl * A {@link PooledObjectFactory} that creates * {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnection PoolableConnection}s. * - * @author John D. McNally * @since 2.0 */ class CPDSConnectionFactory - implements PooledObjectFactory<PooledConnectionAndInfo>, - ConnectionEventListener, PooledConnectionManager { + implements PooledObjectFactory<PooledConnectionAndInfo>, ConnectionEventListener, PooledConnectionManager { - private static final String NO_KEY_MESSAGE - = "close() was called on a Connection, but " - + "I have no record of the underlying PooledConnection."; - - private final ConnectionPoolDataSource _cpds; - private final String _validationQuery; - private final int _validationQueryTimeout; - private final boolean _rollbackAfterValidation; - private ObjectPool<PooledConnectionAndInfo> _pool; - private final String _username; - private String _password = null; - private long maxConnLifetimeMillis = -1; + private static final String NO_KEY_MESSAGE = "close() was called on a Connection, but I have no record of the underlying PooledConnection."; + private final ConnectionPoolDataSource cpds; + private final String validationQuery; + private final int validationQueryTimeoutSeconds; + private final boolean rollbackAfterValidation; + private ObjectPool<PooledConnectionAndInfo> pool; + private final String userName; + private char[] userPassword; + private long maxConnLifetimeMillis = -1; /** - * Map of PooledConnections for which close events are ignored. - * Connections are muted when they are being validated. + * Map of PooledConnections for which close events are ignored. Connections are muted when they are being validated. */ - private final Set<PooledConnection> validatingSet = - Collections.newSetFromMap(new ConcurrentHashMap<PooledConnection,Boolean>()); + private final Set<PooledConnection> validatingSet = Collections + .newSetFromMap(new ConcurrentHashMap<PooledConnection, Boolean>()); /** * Map of PooledConnectionAndInfo instances */ - private final Map<PooledConnection, PooledConnectionAndInfo> pcMap = - new ConcurrentHashMap<>(); + private final Map<PooledConnection, PooledConnectionAndInfo> pcMap = new ConcurrentHashMap<>(); /** - * Create a new {@code PoolableConnectionFactory}. + * Creates a new {@code PoolableConnectionFactory}. * - * @param cpds the ConnectionPoolDataSource from which to obtain - * PooledConnection's - * @param validationQuery a query to use to {@link #validateObject - * validate} {@link Connection}s. Should return at least one row. - * May be {@code null} in which case {@link Connection#isValid(int)} will - * be used to validate connections. - * @param validationQueryTimeout Timeout in seconds before validation fails - * @param rollbackAfterValidation whether a rollback should be issued - * after {@link #validateObject validating} {@link Connection}s. - * @param username - * @param password - */ - public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, - final String validationQuery, - final int validationQueryTimeout, - final boolean rollbackAfterValidation, - final String username, - final String password) { - _cpds = cpds; - _validationQuery = validationQuery; - _validationQueryTimeout = validationQueryTimeout; - _username = username; - _password = password; - _rollbackAfterValidation = rollbackAfterValidation; + * @param cpds + * the ConnectionPoolDataSource from which to obtain PooledConnection's + * @param validationQuery + * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one + * row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate + * connections. + * @param validationQueryTimeoutSeconds + * Timeout in seconds before validation fails + * @param rollbackAfterValidation + * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s. + * @param userName + * The user name to use to create connections + * @param userPassword + * The password to use to create connections + * @since 2.4.0 + */ + public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, + final int validationQueryTimeoutSeconds, final boolean rollbackAfterValidation, final String userName, + final char[] userPassword) { + this.cpds = cpds; + this.validationQuery = validationQuery; + this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds; + this.userName = userName; + this.userPassword = userPassword; + this.rollbackAfterValidation = rollbackAfterValidation; + } + + /** + * Creates a new {@code PoolableConnectionFactory}. + * + * @param cpds + * the ConnectionPoolDataSource from which to obtain PooledConnection's + * @param validationQuery + * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one + * row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate + * connections. + * @param validationQueryTimeoutSeconds + * Timeout in seconds before validation fails + * @param rollbackAfterValidation + * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s. + * @param userName + * The user name to use to create connections + * @param userPassword + * The password to use to create connections + */ + public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, + final int validationQueryTimeoutSeconds, final boolean rollbackAfterValidation, final String userName, + final String userPassword) { + this(cpds, validationQuery, validationQueryTimeoutSeconds, rollbackAfterValidation, userName, + Utils.toCharArray(userPassword)); } /** @@ -109,16 +128,16 @@ class CPDSConnectionFactory * @return ObjectPool managing pooled connections */ public ObjectPool<PooledConnectionAndInfo> getPool() { - return _pool; + return pool; } /** * - * @param pool the {@link ObjectPool} in which to pool those {@link - * Connection}s + * @param pool + * the {@link ObjectPool} in which to pool those {@link Connection}s */ public void setPool(final ObjectPool<PooledConnectionAndInfo> pool) { - this._pool = pool; + this.pool = pool; } @Override @@ -126,10 +145,10 @@ class CPDSConnectionFactory PooledConnectionAndInfo pci; try { PooledConnection pc = null; - if (_username == null) { - pc = _cpds.getPooledConnection(); + if (userName == null) { + pc = cpds.getPooledConnection(); } else { - pc = _cpds.getPooledConnection(_username, _password); + pc = cpds.getPooledConnection(userName, Utils.toString(userPassword)); } if (pc == null) { @@ -139,7 +158,7 @@ class CPDSConnectionFactory // should we add this object as a listener or the pool. // consider the validateObject method in decision pc.addConnectionEventListener(this); - pci = new PooledConnectionAndInfo(pc, _username, _password); + pci = new PooledConnectionAndInfo(pc, userName, userPassword); pcMap.put(pc, pci); } catch (final SQLException e) { throw new RuntimeException(e.getMessage()); @@ -155,7 +174,7 @@ class CPDSConnectionFactory doDestroyObject(p.getObject()); } - private void doDestroyObject(final PooledConnectionAndInfo pci) throws Exception{ + private void doDestroyObject(final PooledConnectionAndInfo pci) throws Exception { final PooledConnection pc = pci.getPooledConnection(); pc.removeConnectionEventListener(this); pcMap.remove(pc); @@ -173,14 +192,14 @@ class CPDSConnectionFactory final PooledConnection pconn = p.getObject().getPooledConnection(); Connection conn = null; validatingSet.add(pconn); - if (null == _validationQuery) { - int timeout = _validationQueryTimeout; - if (timeout < 0) { - timeout = 0; + if (null == validationQuery) { + int timeoutSeconds = validationQueryTimeoutSeconds; + if (timeoutSeconds < 0) { + timeoutSeconds = 0; } try { conn = pconn.getConnection(); - valid = conn.isValid(timeout); + valid = conn.isValid(timeoutSeconds); } catch (final SQLException e) { valid = false; } finally { @@ -198,13 +217,13 @@ class CPDSConnectionFactory try { conn = pconn.getConnection(); stmt = conn.createStatement(); - rset = stmt.executeQuery(_validationQuery); + rset = stmt.executeQuery(validationQuery); if (rset.next()) { valid = true; } else { valid = false; } - if (_rollbackAfterValidation) { + if (rollbackAfterValidation) { conn.rollback(); } } catch (final Exception e) { @@ -220,14 +239,12 @@ class CPDSConnectionFactory } @Override - public void passivateObject(final PooledObject<PooledConnectionAndInfo> p) - throws Exception { + public void passivateObject(final PooledObject<PooledConnectionAndInfo> p) throws Exception { validateLifetime(p); } @Override - public void activateObject(final PooledObject<PooledConnectionAndInfo> p) - throws Exception { + public void activateObject(final PooledObject<PooledConnectionAndInfo> p) throws Exception { validateLifetime(p); } @@ -236,10 +253,9 @@ class CPDSConnectionFactory // *********************************************************************** /** - * This will be called if the Connection returned by the getConnection - * method came from a PooledConnection, and the user calls the close() - * method of this connection object. What we need to do here is to - * release this PooledConnection from our pool... + * This will be called if the Connection returned by the getConnection method came from a PooledConnection, and the + * user calls the close() method of this connection object. What we need to do here is to release this + * PooledConnection from our pool... */ @Override public void connectionClosed(final ConnectionEvent event) { @@ -253,16 +269,14 @@ class CPDSConnectionFactory } try { - _pool.returnObject(pci); + pool.returnObject(pci); } catch (final Exception e) { - System.err.println("CLOSING DOWN CONNECTION AS IT COULD " - + "NOT BE RETURNED TO THE POOL"); + System.err.println("CLOSING DOWN CONNECTION AS IT COULD " + "NOT BE RETURNED TO THE POOL"); pc.removeConnectionEventListener(this); try { doDestroyObject(pci); } catch (final Exception e2) { - System.err.println("EXCEPTION WHILE DESTROYING OBJECT " - + pci); + System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci); e2.printStackTrace(); } } @@ -270,16 +284,13 @@ class CPDSConnectionFactory } /** - * If a fatal error occurs, close the underlying physical connection so as - * not to be returned in the future + * If a fatal error occurs, close the underlying physical connection so as not to be returned in the future */ @Override public void connectionErrorOccurred(final ConnectionEvent event) { - final PooledConnection pc = (PooledConnection)event.getSource(); + final PooledConnection pc = (PooledConnection) event.getSource(); if (null != event.getSQLException()) { - System.err.println( - "CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR (" - + event.getSQLException() + ")"); + System.err.println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR (" + event.getSQLException() + ")"); } pc.removeConnectionEventListener(this); @@ -288,7 +299,7 @@ class CPDSConnectionFactory throw new IllegalStateException(NO_KEY_MESSAGE); } try { - _pool.invalidateObject(pci); + pool.invalidateObject(pci); } catch (final Exception e) { System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci); e.printStackTrace(); @@ -300,10 +311,9 @@ class CPDSConnectionFactory // *********************************************************************** /** - * Invalidates the PooledConnection in the pool. The CPDSConnectionFactory - * closes the connection and pool counters are updated appropriately. - * Also closes the pool. This ensures that all idle connections are closed - * and connections that are checked out are closed on return. + * Invalidates the PooledConnection in the pool. The CPDSConnectionFactory closes the connection and pool counters + * are updated appropriately. Also closes the pool. This ensures that all idle connections are closed and + * connections that are checked out are closed on return. */ @Override public void invalidate(final PooledConnection pc) throws SQLException { @@ -312,8 +322,8 @@ class CPDSConnectionFactory throw new IllegalStateException(NO_KEY_MESSAGE); } try { - _pool.invalidateObject(pci); // Destroy instance and update pool counters - _pool.close(); // Clear any other instances in this pool and kill others as they come back + pool.invalidateObject(pci); // Destroy instance and update pool counters + pool.close(); // Clear any other instances in this pool and kill others as they come back } catch (final Exception ex) { throw new SQLException("Error invalidating connection", ex); } @@ -322,49 +332,58 @@ class CPDSConnectionFactory /** * Sets the database password used when creating new connections. * - * @param password new password + * @param userPassword + * new password + */ + public synchronized void setPassword(final char[] userPassword) { + this.userPassword = userPassword; + } + + /** + * Sets the database password used when creating new connections. + * + * @param userPassword + * new password */ @Override - public synchronized void setPassword(final String password) { - _password = password; + public synchronized void setPassword(final String userPassword) { + this.userPassword = Utils.toCharArray(userPassword); } /** - * Sets the maximum lifetime in milliseconds of a connection after which the - * connection will always fail activation, passivation and validation. A - * value of zero or less indicates an infinite lifetime. The default value - * is -1. + * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation, + * passivation and validation. + * + * @param maxConnLifetimeMillis + * A value of zero or less indicates an infinite lifetime. The default value is -1. */ public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) { this.maxConnLifetimeMillis = maxConnLifetimeMillis; } /** - * Verifies that the username matches the user whose connections are being managed by this - * factory and closes the pool if this is the case; otherwise does nothing. + * Verifies that the user name matches the user whose connections are being managed by this factory and closes the + * pool if this is the case; otherwise does nothing. */ @Override - public void closePool(final String username) throws SQLException { + public void closePool(final String userName) throws SQLException { synchronized (this) { - if (username == null || !username.equals(_username)) { + if (userName == null || !userName.equals(this.userName)) { return; } } try { - _pool.close(); + pool.close(); } catch (final Exception ex) { throw new SQLException("Error closing connection pool", ex); } } - private void validateLifetime(final PooledObject<PooledConnectionAndInfo> p) - throws Exception { + private void validateLifetime(final PooledObject<PooledConnectionAndInfo> p) throws Exception { if (maxConnLifetimeMillis > 0) { final long lifetime = System.currentTimeMillis() - p.getCreateTime(); if (lifetime > maxConnLifetimeMillis) { - throw new Exception(Utils.getMessage( - "connectionFactory.lifetimeExceeded", - Long.valueOf(lifetime), + throw new Exception(Utils.getMessage("connectionFactory.lifetimeExceeded", Long.valueOf(lifetime), Long.valueOf(maxConnLifetimeMillis))); } } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org