DelegatingStatement.java
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.apache.commons.dbcp2;
- import java.sql.Connection;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.SQLWarning;
- import java.sql.Statement;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Objects;
- /**
- * A base delegating implementation of {@link Statement}.
- * <p>
- * All of the methods from the {@link Statement} interface simply check to see that the {@link Statement} is active, and
- * call the corresponding method on the "delegate" provided in my constructor.
- * <p>
- * Extends AbandonedTrace to implement Statement tracking and logging of code which created the Statement. Tracking the
- * Statement ensures that the Connection which created it can close any open Statement's on Connection close.
- *
- * @since 2.0
- */
- public class DelegatingStatement extends AbandonedTrace implements Statement {
- /** My delegate. */
- private Statement statement;
- /** The connection that created me. **/
- private DelegatingConnection<?> connection;
- private boolean closed;
- /**
- * Create a wrapper for the Statement which traces this Statement to the Connection which created it and the code
- * which created it.
- *
- * @param statement
- * the {@link Statement} to delegate all calls to.
- * @param connection
- * the {@link DelegatingConnection} that created this statement.
- */
- public DelegatingStatement(final DelegatingConnection<?> connection, final Statement statement) {
- super(connection);
- this.statement = statement;
- this.connection = connection;
- }
- /**
- *
- * @throws SQLException
- * thrown by the delegating statement.
- * @since 2.4.0 made public, was protected in 2.3.0.
- */
- public void activate() throws SQLException {
- if (statement instanceof DelegatingStatement) {
- ((DelegatingStatement) statement).activate();
- }
- }
- @Override
- public void addBatch(final String sql) throws SQLException {
- checkOpen();
- try {
- statement.addBatch(sql);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
- @Override
- public void cancel() throws SQLException {
- checkOpen();
- try {
- statement.cancel();
- } catch (final SQLException e) {
- handleException(e);
- }
- }
- protected void checkOpen() throws SQLException {
- if (isClosed()) {
- throw new SQLException(this.getClass().getName() + " with address: \"" + toString() + "\" is closed.");
- }
- }
- @Override
- public void clearBatch() throws SQLException {
- checkOpen();
- try {
- statement.clearBatch();
- } catch (final SQLException e) {
- handleException(e);
- }
- }
- @Override
- public void clearWarnings() throws SQLException {
- checkOpen();
- try {
- statement.clearWarnings();
- } catch (final SQLException e) {
- handleException(e);
- }
- }
- /**
- * Close this DelegatingStatement, and close any ResultSets that were not explicitly closed.
- */
- @Override
- public void close() throws SQLException {
- if (isClosed()) {
- return;
- }
- final List<Exception> thrownList = new ArrayList<>();
- try {
- if (connection != null) {
- connection.removeTrace(this);
- connection = null;
- }
- // The JDBC spec requires that a statement close any open
- // ResultSet's when it is closed.
- // FIXME The PreparedStatement we're wrapping should handle this for us.
- // See bug 17301 for what could happen when ResultSets are closed twice.
- final List<AbandonedTrace> traceList = getTrace();
- if (traceList != null) {
- traceList.forEach(trace -> trace.close(e -> {
- if (connection != null) {
- // Does not rethrow e.
- connection.handleExceptionNoThrow(e);
- }
- thrownList.add(e);
- }));
- clearTrace();
- }
- Utils.close(statement, e -> {
- if (connection != null) {
- // Does not rethrow e.
- connection.handleExceptionNoThrow(e);
- }
- thrownList.add(e);
- });
- } finally {
- closed = true;
- statement = null;
- if (!thrownList.isEmpty()) {
- throw new SQLExceptionList(thrownList);
- }
- }
- }
- @Override
- public void closeOnCompletion() throws SQLException {
- checkOpen();
- try {
- Jdbc41Bridge.closeOnCompletion(statement);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
- @Override
- public boolean execute(final String sql) throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return statement.execute(sql);
- } catch (final SQLException e) {
- handleException(e);
- return false;
- }
- }
- @Override
- public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return statement.execute(sql, autoGeneratedKeys);
- } catch (final SQLException e) {
- handleException(e);
- return false;
- }
- }
- @Override
- public boolean execute(final String sql, final int[] columnIndexes) throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return statement.execute(sql, columnIndexes);
- } catch (final SQLException e) {
- handleException(e);
- return false;
- }
- }
- @Override
- public boolean execute(final String sql, final String[] columnNames) throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return statement.execute(sql, columnNames);
- } catch (final SQLException e) {
- handleException(e);
- return false;
- }
- }
- @Override
- public int[] executeBatch() throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return statement.executeBatch();
- } catch (final SQLException e) {
- handleException(e);
- throw new AssertionError();
- }
- }
- /**
- * @since 2.5.0
- */
- @Override
- public long[] executeLargeBatch() throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return statement.executeLargeBatch();
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
- /**
- * @since 2.5.0
- */
- @Override
- public long executeLargeUpdate(final String sql) throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return statement.executeLargeUpdate(sql);
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- /**
- * @since 2.5.0
- */
- @Override
- public long executeLargeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return statement.executeLargeUpdate(sql, autoGeneratedKeys);
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- /**
- * @since 2.5.0
- */
- @Override
- public long executeLargeUpdate(final String sql, final int[] columnIndexes) throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return statement.executeLargeUpdate(sql, columnIndexes);
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- /**
- * @since 2.5.0
- */
- @Override
- public long executeLargeUpdate(final String sql, final String[] columnNames) throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return statement.executeLargeUpdate(sql, columnNames);
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public ResultSet executeQuery(final String sql) throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return DelegatingResultSet.wrapResultSet(this, statement.executeQuery(sql));
- } catch (final SQLException e) {
- handleException(e);
- throw new AssertionError();
- }
- }
- @Override
- public int executeUpdate(final String sql) throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return statement.executeUpdate(sql);
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @Override
- public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return statement.executeUpdate(sql, autoGeneratedKeys);
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @Override
- public int executeUpdate(final String sql, final int[] columnIndexes) throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return statement.executeUpdate(sql, columnIndexes);
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @Override
- public int executeUpdate(final String sql, final String[] columnNames) throws SQLException {
- checkOpen();
- setLastUsedInParent();
- try {
- return statement.executeUpdate(sql, columnNames);
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @Override
- protected void finalize() throws Throwable {
- // This is required because of statement pooling. The poolable
- // statements will always be strongly held by the statement pool. If the
- // delegating statements that wrap the poolable statement are not
- // strongly held they will be garbage collected but at that point the
- // poolable statements need to be returned to the pool else there will
- // be a leak of statements from the pool. Closing this statement will
- // close all the wrapped statements and return any poolable statements
- // to the pool.
- close();
- super.finalize();
- }
- @Override
- public Connection getConnection() throws SQLException {
- checkOpen();
- return getConnectionInternal(); // return the delegating connection that created this
- }
- protected DelegatingConnection<?> getConnectionInternal() {
- return connection;
- }
- /**
- * Returns my underlying {@link Statement}.
- *
- * @return my underlying {@link Statement}.
- * @see #getInnermostDelegate
- */
- public Statement getDelegate() {
- return statement;
- }
- @Override
- public int getFetchDirection() throws SQLException {
- checkOpen();
- try {
- return statement.getFetchDirection();
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @Override
- public int getFetchSize() throws SQLException {
- checkOpen();
- try {
- return statement.getFetchSize();
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public ResultSet getGeneratedKeys() throws SQLException {
- checkOpen();
- try {
- return DelegatingResultSet.wrapResultSet(this, statement.getGeneratedKeys());
- } catch (final SQLException e) {
- handleException(e);
- throw new AssertionError();
- }
- }
- /**
- * If my underlying {@link Statement} is not a {@code DelegatingStatement}, returns it, otherwise recursively
- * invokes this method on my delegate.
- * <p>
- * Hence this method will return the first delegate that is not a {@code DelegatingStatement} or {@code null} when
- * no non-{@code DelegatingStatement} delegate can be found by traversing this chain.
- * </p>
- * <p>
- * This method is useful when you may have nested {@code DelegatingStatement}s, and you want to make sure to obtain
- * a "genuine" {@link Statement}.
- * </p>
- *
- * @return The innermost delegate, may return null.
- * @see #getDelegate
- */
- @SuppressWarnings("resource")
- public Statement getInnermostDelegate() {
- Statement stmt = statement;
- while (stmt instanceof DelegatingStatement) {
- stmt = ((DelegatingStatement) stmt).getDelegate();
- if (this == stmt) {
- return null;
- }
- }
- return stmt;
- }
- /**
- * @since 2.5.0
- */
- @Override
- public long getLargeMaxRows() throws SQLException {
- checkOpen();
- try {
- return statement.getLargeMaxRows();
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- /**
- * @since 2.5.0
- */
- @Override
- public long getLargeUpdateCount() throws SQLException {
- checkOpen();
- try {
- return statement.getLargeUpdateCount();
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @Override
- public int getMaxFieldSize() throws SQLException {
- checkOpen();
- try {
- return statement.getMaxFieldSize();
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @Override
- public int getMaxRows() throws SQLException {
- checkOpen();
- try {
- return statement.getMaxRows();
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @Override
- public boolean getMoreResults() throws SQLException {
- checkOpen();
- try {
- return statement.getMoreResults();
- } catch (final SQLException e) {
- handleException(e);
- return false;
- }
- }
- @Override
- public boolean getMoreResults(final int current) throws SQLException {
- checkOpen();
- try {
- return statement.getMoreResults(current);
- } catch (final SQLException e) {
- handleException(e);
- return false;
- }
- }
- @Override
- public int getQueryTimeout() throws SQLException {
- checkOpen();
- try {
- return statement.getQueryTimeout();
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public ResultSet getResultSet() throws SQLException {
- checkOpen();
- try {
- return DelegatingResultSet.wrapResultSet(this, statement.getResultSet());
- } catch (final SQLException e) {
- handleException(e);
- throw new AssertionError();
- }
- }
- @Override
- public int getResultSetConcurrency() throws SQLException {
- checkOpen();
- try {
- return statement.getResultSetConcurrency();
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @Override
- public int getResultSetHoldability() throws SQLException {
- checkOpen();
- try {
- return statement.getResultSetHoldability();
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @Override
- public int getResultSetType() throws SQLException {
- checkOpen();
- try {
- return statement.getResultSetType();
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @Override
- public int getUpdateCount() throws SQLException {
- checkOpen();
- try {
- return statement.getUpdateCount();
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
- @Override
- public SQLWarning getWarnings() throws SQLException {
- checkOpen();
- try {
- return statement.getWarnings();
- } catch (final SQLException e) {
- handleException(e);
- throw new AssertionError();
- }
- }
- protected void handleException(final SQLException e) throws SQLException {
- if (connection == null) {
- throw e;
- }
- connection.handleException(e);
- }
- /*
- * Note: This method was protected prior to JDBC 4.
- */
- @Override
- public boolean isClosed() throws SQLException {
- return closed;
- }
- protected boolean isClosedInternal() {
- return closed;
- }
- @Override
- public boolean isCloseOnCompletion() throws SQLException {
- checkOpen();
- try {
- return Jdbc41Bridge.isCloseOnCompletion(statement);
- } catch (final SQLException e) {
- handleException(e);
- return false;
- }
- }
- @Override
- public boolean isPoolable() throws SQLException {
- checkOpen();
- try {
- return statement.isPoolable();
- } catch (final SQLException e) {
- handleException(e);
- return false;
- }
- }
- @Override
- public boolean isWrapperFor(final Class<?> iface) throws SQLException {
- if (iface.isAssignableFrom(getClass())) {
- return true;
- }
- if (iface.isAssignableFrom(statement.getClass())) {
- return true;
- }
- return statement.isWrapperFor(iface);
- }
- /**
- *
- * @throws SQLException
- * thrown by the delegating statement.
- * @since 2.4.0 made public, was protected in 2.3.0.
- */
- public void passivate() throws SQLException {
- if (statement instanceof DelegatingStatement) {
- ((DelegatingStatement) statement).passivate();
- }
- }
- protected void setClosedInternal(final boolean closed) {
- this.closed = closed;
- }
- @Override
- public void setCursorName(final String name) throws SQLException {
- checkOpen();
- try {
- statement.setCursorName(name);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
- /**
- * Sets my delegate.
- *
- * @param statement
- * my delegate.
- */
- public void setDelegate(final Statement statement) {
- this.statement = statement;
- }
- @Override
- public void setEscapeProcessing(final boolean enable) throws SQLException {
- checkOpen();
- try {
- statement.setEscapeProcessing(enable);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
- @Override
- public void setFetchDirection(final int direction) throws SQLException {
- checkOpen();
- try {
- statement.setFetchDirection(direction);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
- @Override
- public void setFetchSize(final int rows) throws SQLException {
- checkOpen();
- try {
- statement.setFetchSize(rows);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
- /**
- * @since 2.5.0
- */
- @Override
- public void setLargeMaxRows(final long max) throws SQLException {
- checkOpen();
- try {
- statement.setLargeMaxRows(max);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
- private void setLastUsedInParent() {
- if (connection != null) {
- connection.setLastUsed();
- }
- }
- @Override
- public void setMaxFieldSize(final int max) throws SQLException {
- checkOpen();
- try {
- statement.setMaxFieldSize(max);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
- @Override
- public void setMaxRows(final int max) throws SQLException {
- checkOpen();
- try {
- statement.setMaxRows(max);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
- @Override
- public void setPoolable(final boolean poolable) throws SQLException {
- checkOpen();
- try {
- statement.setPoolable(poolable);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
- @Override
- public void setQueryTimeout(final int seconds) throws SQLException {
- checkOpen();
- try {
- statement.setQueryTimeout(seconds);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
- /**
- * Returns a String representation of this object.
- *
- * @return String
- */
- @Override
- public synchronized String toString() {
- return Objects.toString(statement, "NULL");
- }
- @Override
- public <T> T unwrap(final Class<T> iface) throws SQLException {
- if (iface.isAssignableFrom(getClass())) {
- return iface.cast(this);
- }
- if (iface.isAssignableFrom(statement.getClass())) {
- return iface.cast(statement);
- }
- return statement.unwrap(iface);
- }
- }