DelegatingStatement.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;

  18. import java.sql.Connection;
  19. import java.sql.ResultSet;
  20. import java.sql.SQLException;
  21. import java.sql.SQLWarning;
  22. import java.sql.Statement;
  23. import java.util.ArrayList;
  24. import java.util.List;
  25. import java.util.Objects;

  26. /**
  27.  * A base delegating implementation of {@link Statement}.
  28.  * <p>
  29.  * All of the methods from the {@link Statement} interface simply check to see that the {@link Statement} is active, and
  30.  * call the corresponding method on the "delegate" provided in my constructor.
  31.  * <p>
  32.  * Extends AbandonedTrace to implement Statement tracking and logging of code which created the Statement. Tracking the
  33.  * Statement ensures that the Connection which created it can close any open Statement's on Connection close.
  34.  *
  35.  * @since 2.0
  36.  */
  37. public class DelegatingStatement extends AbandonedTrace implements Statement {

  38.     /** My delegate. */
  39.     private Statement statement;

  40.     /** The connection that created me. **/
  41.     private DelegatingConnection<?> connection;

  42.     private boolean closed;

  43.     /**
  44.      * Create a wrapper for the Statement which traces this Statement to the Connection which created it and the code
  45.      * which created it.
  46.      *
  47.      * @param statement
  48.      *            the {@link Statement} to delegate all calls to.
  49.      * @param connection
  50.      *            the {@link DelegatingConnection} that created this statement.
  51.      */
  52.     public DelegatingStatement(final DelegatingConnection<?> connection, final Statement statement) {
  53.         super(connection);
  54.         this.statement = statement;
  55.         this.connection = connection;
  56.     }

  57.     /**
  58.      *
  59.      * @throws SQLException
  60.      *             thrown by the delegating statement.
  61.      * @since 2.4.0 made public, was protected in 2.3.0.
  62.      */
  63.     public void activate() throws SQLException {
  64.         if (statement instanceof DelegatingStatement) {
  65.             ((DelegatingStatement) statement).activate();
  66.         }
  67.     }

  68.     @Override
  69.     public void addBatch(final String sql) throws SQLException {
  70.         checkOpen();
  71.         try {
  72.             statement.addBatch(sql);
  73.         } catch (final SQLException e) {
  74.             handleException(e);
  75.         }
  76.     }

  77.     @Override
  78.     public void cancel() throws SQLException {
  79.         checkOpen();
  80.         try {
  81.             statement.cancel();
  82.         } catch (final SQLException e) {
  83.             handleException(e);
  84.         }
  85.     }

  86.     protected void checkOpen() throws SQLException {
  87.         if (isClosed()) {
  88.             throw new SQLException(this.getClass().getName() + " with address: \"" + toString() + "\" is closed.");
  89.         }
  90.     }

  91.     @Override
  92.     public void clearBatch() throws SQLException {
  93.         checkOpen();
  94.         try {
  95.             statement.clearBatch();
  96.         } catch (final SQLException e) {
  97.             handleException(e);
  98.         }
  99.     }

  100.     @Override
  101.     public void clearWarnings() throws SQLException {
  102.         checkOpen();
  103.         try {
  104.             statement.clearWarnings();
  105.         } catch (final SQLException e) {
  106.             handleException(e);
  107.         }
  108.     }

  109.     /**
  110.      * Close this DelegatingStatement, and close any ResultSets that were not explicitly closed.
  111.      */
  112.     @Override
  113.     public void close() throws SQLException {
  114.         if (isClosed()) {
  115.             return;
  116.         }
  117.         final List<Exception> thrownList = new ArrayList<>();
  118.         try {
  119.             if (connection != null) {
  120.                 connection.removeTrace(this);
  121.                 connection = null;
  122.             }

  123.             // The JDBC spec requires that a statement close any open
  124.             // ResultSet's when it is closed.
  125.             // FIXME The PreparedStatement we're wrapping should handle this for us.
  126.             // See bug 17301 for what could happen when ResultSets are closed twice.
  127.             final List<AbandonedTrace> traceList = getTrace();
  128.             if (traceList != null) {
  129.                 traceList.forEach(trace -> trace.close(e -> {
  130.                     if (connection != null) {
  131.                         // Does not rethrow e.
  132.                         connection.handleExceptionNoThrow(e);
  133.                     }
  134.                     thrownList.add(e);
  135.                 }));
  136.                 clearTrace();
  137.             }
  138.             Utils.close(statement, e -> {
  139.                 if (connection != null) {
  140.                     // Does not rethrow e.
  141.                     connection.handleExceptionNoThrow(e);
  142.                 }
  143.                 thrownList.add(e);
  144.             });
  145.         } finally {
  146.             closed = true;
  147.             statement = null;
  148.             if (!thrownList.isEmpty()) {
  149.                 throw new SQLExceptionList(thrownList);
  150.             }
  151.         }
  152.     }

  153.     @Override
  154.     public void closeOnCompletion() throws SQLException {
  155.         checkOpen();
  156.         try {
  157.             Jdbc41Bridge.closeOnCompletion(statement);
  158.         } catch (final SQLException e) {
  159.             handleException(e);
  160.         }
  161.     }

  162.     @Override
  163.     public boolean execute(final String sql) throws SQLException {
  164.         checkOpen();
  165.         setLastUsedInParent();
  166.         try {
  167.             return statement.execute(sql);
  168.         } catch (final SQLException e) {
  169.             handleException(e);
  170.             return false;
  171.         }
  172.     }

  173.     @Override
  174.     public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException {
  175.         checkOpen();
  176.         setLastUsedInParent();
  177.         try {
  178.             return statement.execute(sql, autoGeneratedKeys);
  179.         } catch (final SQLException e) {
  180.             handleException(e);
  181.             return false;
  182.         }
  183.     }

  184.     @Override
  185.     public boolean execute(final String sql, final int[] columnIndexes) throws SQLException {
  186.         checkOpen();
  187.         setLastUsedInParent();
  188.         try {
  189.             return statement.execute(sql, columnIndexes);
  190.         } catch (final SQLException e) {
  191.             handleException(e);
  192.             return false;
  193.         }
  194.     }

  195.     @Override
  196.     public boolean execute(final String sql, final String[] columnNames) throws SQLException {
  197.         checkOpen();
  198.         setLastUsedInParent();
  199.         try {
  200.             return statement.execute(sql, columnNames);
  201.         } catch (final SQLException e) {
  202.             handleException(e);
  203.             return false;
  204.         }
  205.     }

  206.     @Override
  207.     public int[] executeBatch() throws SQLException {
  208.         checkOpen();
  209.         setLastUsedInParent();
  210.         try {
  211.             return statement.executeBatch();
  212.         } catch (final SQLException e) {
  213.             handleException(e);
  214.             throw new AssertionError();
  215.         }
  216.     }

  217.     /**
  218.      * @since 2.5.0
  219.      */
  220.     @Override
  221.     public long[] executeLargeBatch() throws SQLException {
  222.         checkOpen();
  223.         setLastUsedInParent();
  224.         try {
  225.             return statement.executeLargeBatch();
  226.         } catch (final SQLException e) {
  227.             handleException(e);
  228.             return null;
  229.         }
  230.     }

  231.     /**
  232.      * @since 2.5.0
  233.      */
  234.     @Override
  235.     public long executeLargeUpdate(final String sql) throws SQLException {
  236.         checkOpen();
  237.         setLastUsedInParent();
  238.         try {
  239.             return statement.executeLargeUpdate(sql);
  240.         } catch (final SQLException e) {
  241.             handleException(e);
  242.             return 0;
  243.         }
  244.     }

  245.     /**
  246.      * @since 2.5.0
  247.      */
  248.     @Override
  249.     public long executeLargeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException {
  250.         checkOpen();
  251.         setLastUsedInParent();
  252.         try {
  253.             return statement.executeLargeUpdate(sql, autoGeneratedKeys);
  254.         } catch (final SQLException e) {
  255.             handleException(e);
  256.             return 0;
  257.         }
  258.     }

  259.     /**
  260.      * @since 2.5.0
  261.      */
  262.     @Override
  263.     public long executeLargeUpdate(final String sql, final int[] columnIndexes) throws SQLException {
  264.         checkOpen();
  265.         setLastUsedInParent();
  266.         try {
  267.             return statement.executeLargeUpdate(sql, columnIndexes);
  268.         } catch (final SQLException e) {
  269.             handleException(e);
  270.             return 0;
  271.         }
  272.     }

  273.     /**
  274.      * @since 2.5.0
  275.      */
  276.     @Override
  277.     public long executeLargeUpdate(final String sql, final String[] columnNames) throws SQLException {
  278.         checkOpen();
  279.         setLastUsedInParent();
  280.         try {
  281.             return statement.executeLargeUpdate(sql, columnNames);
  282.         } catch (final SQLException e) {
  283.             handleException(e);
  284.             return 0;
  285.         }
  286.     }

  287.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  288.     @Override
  289.     public ResultSet executeQuery(final String sql) throws SQLException {
  290.         checkOpen();
  291.         setLastUsedInParent();
  292.         try {
  293.             return DelegatingResultSet.wrapResultSet(this, statement.executeQuery(sql));
  294.         } catch (final SQLException e) {
  295.             handleException(e);
  296.             throw new AssertionError();
  297.         }
  298.     }

  299.     @Override
  300.     public int executeUpdate(final String sql) throws SQLException {
  301.         checkOpen();
  302.         setLastUsedInParent();
  303.         try {
  304.             return statement.executeUpdate(sql);
  305.         } catch (final SQLException e) {
  306.             handleException(e);
  307.             return 0;
  308.         }
  309.     }

  310.     @Override
  311.     public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException {
  312.         checkOpen();
  313.         setLastUsedInParent();
  314.         try {
  315.             return statement.executeUpdate(sql, autoGeneratedKeys);
  316.         } catch (final SQLException e) {
  317.             handleException(e);
  318.             return 0;
  319.         }
  320.     }

  321.     @Override
  322.     public int executeUpdate(final String sql, final int[] columnIndexes) throws SQLException {
  323.         checkOpen();
  324.         setLastUsedInParent();
  325.         try {
  326.             return statement.executeUpdate(sql, columnIndexes);
  327.         } catch (final SQLException e) {
  328.             handleException(e);
  329.             return 0;
  330.         }
  331.     }

  332.     @Override
  333.     public int executeUpdate(final String sql, final String[] columnNames) throws SQLException {
  334.         checkOpen();
  335.         setLastUsedInParent();
  336.         try {
  337.             return statement.executeUpdate(sql, columnNames);
  338.         } catch (final SQLException e) {
  339.             handleException(e);
  340.             return 0;
  341.         }
  342.     }

  343.     @Override
  344.     protected void finalize() throws Throwable {
  345.         // This is required because of statement pooling. The poolable
  346.         // statements will always be strongly held by the statement pool. If the
  347.         // delegating statements that wrap the poolable statement are not
  348.         // strongly held they will be garbage collected but at that point the
  349.         // poolable statements need to be returned to the pool else there will
  350.         // be a leak of statements from the pool. Closing this statement will
  351.         // close all the wrapped statements and return any poolable statements
  352.         // to the pool.
  353.         close();
  354.         super.finalize();
  355.     }

  356.     @Override
  357.     public Connection getConnection() throws SQLException {
  358.         checkOpen();
  359.         return getConnectionInternal(); // return the delegating connection that created this
  360.     }

  361.     protected DelegatingConnection<?> getConnectionInternal() {
  362.         return connection;
  363.     }

  364.     /**
  365.      * Returns my underlying {@link Statement}.
  366.      *
  367.      * @return my underlying {@link Statement}.
  368.      * @see #getInnermostDelegate
  369.      */
  370.     public Statement getDelegate() {
  371.         return statement;
  372.     }

  373.     @Override
  374.     public int getFetchDirection() throws SQLException {
  375.         checkOpen();
  376.         try {
  377.             return statement.getFetchDirection();
  378.         } catch (final SQLException e) {
  379.             handleException(e);
  380.             return 0;
  381.         }
  382.     }

  383.     @Override
  384.     public int getFetchSize() throws SQLException {
  385.         checkOpen();
  386.         try {
  387.             return statement.getFetchSize();
  388.         } catch (final SQLException e) {
  389.             handleException(e);
  390.             return 0;
  391.         }
  392.     }

  393.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  394.     @Override
  395.     public ResultSet getGeneratedKeys() throws SQLException {
  396.         checkOpen();
  397.         try {
  398.             return DelegatingResultSet.wrapResultSet(this, statement.getGeneratedKeys());
  399.         } catch (final SQLException e) {
  400.             handleException(e);
  401.             throw new AssertionError();
  402.         }
  403.     }

  404.     /**
  405.      * If my underlying {@link Statement} is not a {@code DelegatingStatement}, returns it, otherwise recursively
  406.      * invokes this method on my delegate.
  407.      * <p>
  408.      * Hence this method will return the first delegate that is not a {@code DelegatingStatement} or {@code null} when
  409.      * no non-{@code DelegatingStatement} delegate can be found by traversing this chain.
  410.      * </p>
  411.      * <p>
  412.      * This method is useful when you may have nested {@code DelegatingStatement}s, and you want to make sure to obtain
  413.      * a "genuine" {@link Statement}.
  414.      * </p>
  415.      *
  416.      * @return The innermost delegate, may return null.
  417.      * @see #getDelegate
  418.      */
  419.     @SuppressWarnings("resource")
  420.     public Statement getInnermostDelegate() {
  421.         Statement stmt = statement;
  422.         while (stmt instanceof DelegatingStatement) {
  423.             stmt = ((DelegatingStatement) stmt).getDelegate();
  424.             if (this == stmt) {
  425.                 return null;
  426.             }
  427.         }
  428.         return stmt;
  429.     }

  430.     /**
  431.      * @since 2.5.0
  432.      */
  433.     @Override
  434.     public long getLargeMaxRows() throws SQLException {
  435.         checkOpen();
  436.         try {
  437.             return statement.getLargeMaxRows();
  438.         } catch (final SQLException e) {
  439.             handleException(e);
  440.             return 0;
  441.         }
  442.     }

  443.     /**
  444.      * @since 2.5.0
  445.      */
  446.     @Override
  447.     public long getLargeUpdateCount() throws SQLException {
  448.         checkOpen();
  449.         try {
  450.             return statement.getLargeUpdateCount();
  451.         } catch (final SQLException e) {
  452.             handleException(e);
  453.             return 0;
  454.         }
  455.     }

  456.     @Override
  457.     public int getMaxFieldSize() throws SQLException {
  458.         checkOpen();
  459.         try {
  460.             return statement.getMaxFieldSize();
  461.         } catch (final SQLException e) {
  462.             handleException(e);
  463.             return 0;
  464.         }
  465.     }

  466.     @Override
  467.     public int getMaxRows() throws SQLException {
  468.         checkOpen();
  469.         try {
  470.             return statement.getMaxRows();
  471.         } catch (final SQLException e) {
  472.             handleException(e);
  473.             return 0;
  474.         }
  475.     }

  476.     @Override
  477.     public boolean getMoreResults() throws SQLException {
  478.         checkOpen();
  479.         try {
  480.             return statement.getMoreResults();
  481.         } catch (final SQLException e) {
  482.             handleException(e);
  483.             return false;
  484.         }
  485.     }

  486.     @Override
  487.     public boolean getMoreResults(final int current) throws SQLException {
  488.         checkOpen();
  489.         try {
  490.             return statement.getMoreResults(current);
  491.         } catch (final SQLException e) {
  492.             handleException(e);
  493.             return false;
  494.         }
  495.     }

  496.     @Override
  497.     public int getQueryTimeout() throws SQLException {
  498.         checkOpen();
  499.         try {
  500.             return statement.getQueryTimeout();
  501.         } catch (final SQLException e) {
  502.             handleException(e);
  503.             return 0;
  504.         }
  505.     }

  506.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  507.     @Override
  508.     public ResultSet getResultSet() throws SQLException {
  509.         checkOpen();
  510.         try {
  511.             return DelegatingResultSet.wrapResultSet(this, statement.getResultSet());
  512.         } catch (final SQLException e) {
  513.             handleException(e);
  514.             throw new AssertionError();
  515.         }
  516.     }

  517.     @Override
  518.     public int getResultSetConcurrency() throws SQLException {
  519.         checkOpen();
  520.         try {
  521.             return statement.getResultSetConcurrency();
  522.         } catch (final SQLException e) {
  523.             handleException(e);
  524.             return 0;
  525.         }
  526.     }

  527.     @Override
  528.     public int getResultSetHoldability() throws SQLException {
  529.         checkOpen();
  530.         try {
  531.             return statement.getResultSetHoldability();
  532.         } catch (final SQLException e) {
  533.             handleException(e);
  534.             return 0;
  535.         }
  536.     }

  537.     @Override
  538.     public int getResultSetType() throws SQLException {
  539.         checkOpen();
  540.         try {
  541.             return statement.getResultSetType();
  542.         } catch (final SQLException e) {
  543.             handleException(e);
  544.             return 0;
  545.         }
  546.     }

  547.     @Override
  548.     public int getUpdateCount() throws SQLException {
  549.         checkOpen();
  550.         try {
  551.             return statement.getUpdateCount();
  552.         } catch (final SQLException e) {
  553.             handleException(e);
  554.             return 0;
  555.         }
  556.     }

  557.     @Override
  558.     public SQLWarning getWarnings() throws SQLException {
  559.         checkOpen();
  560.         try {
  561.             return statement.getWarnings();
  562.         } catch (final SQLException e) {
  563.             handleException(e);
  564.             throw new AssertionError();
  565.         }
  566.     }

  567.     protected void handleException(final SQLException e) throws SQLException {
  568.         if (connection == null) {
  569.             throw e;
  570.         }
  571.         connection.handleException(e);
  572.     }

  573.     /*
  574.      * Note: This method was protected prior to JDBC 4.
  575.      */
  576.     @Override
  577.     public boolean isClosed() throws SQLException {
  578.         return closed;
  579.     }

  580.     protected boolean isClosedInternal() {
  581.         return closed;
  582.     }

  583.     @Override
  584.     public boolean isCloseOnCompletion() throws SQLException {
  585.         checkOpen();
  586.         try {
  587.             return Jdbc41Bridge.isCloseOnCompletion(statement);
  588.         } catch (final SQLException e) {
  589.             handleException(e);
  590.             return false;
  591.         }
  592.     }

  593.     @Override
  594.     public boolean isPoolable() throws SQLException {
  595.         checkOpen();
  596.         try {
  597.             return statement.isPoolable();
  598.         } catch (final SQLException e) {
  599.             handleException(e);
  600.             return false;
  601.         }
  602.     }

  603.     @Override
  604.     public boolean isWrapperFor(final Class<?> iface) throws SQLException {
  605.         if (iface.isAssignableFrom(getClass())) {
  606.             return true;
  607.         }
  608.         if (iface.isAssignableFrom(statement.getClass())) {
  609.             return true;
  610.         }
  611.         return statement.isWrapperFor(iface);
  612.     }

  613.     /**
  614.      *
  615.      * @throws SQLException
  616.      *             thrown by the delegating statement.
  617.      * @since 2.4.0 made public, was protected in 2.3.0.
  618.      */
  619.     public void passivate() throws SQLException {
  620.         if (statement instanceof DelegatingStatement) {
  621.             ((DelegatingStatement) statement).passivate();
  622.         }
  623.     }

  624.     protected void setClosedInternal(final boolean closed) {
  625.         this.closed = closed;
  626.     }

  627.     @Override
  628.     public void setCursorName(final String name) throws SQLException {
  629.         checkOpen();
  630.         try {
  631.             statement.setCursorName(name);
  632.         } catch (final SQLException e) {
  633.             handleException(e);
  634.         }
  635.     }

  636.     /**
  637.      * Sets my delegate.
  638.      *
  639.      * @param statement
  640.      *            my delegate.
  641.      */
  642.     public void setDelegate(final Statement statement) {
  643.         this.statement = statement;
  644.     }

  645.     @Override
  646.     public void setEscapeProcessing(final boolean enable) throws SQLException {
  647.         checkOpen();
  648.         try {
  649.             statement.setEscapeProcessing(enable);
  650.         } catch (final SQLException e) {
  651.             handleException(e);
  652.         }
  653.     }

  654.     @Override
  655.     public void setFetchDirection(final int direction) throws SQLException {
  656.         checkOpen();
  657.         try {
  658.             statement.setFetchDirection(direction);
  659.         } catch (final SQLException e) {
  660.             handleException(e);
  661.         }
  662.     }

  663.     @Override
  664.     public void setFetchSize(final int rows) throws SQLException {
  665.         checkOpen();
  666.         try {
  667.             statement.setFetchSize(rows);
  668.         } catch (final SQLException e) {
  669.             handleException(e);
  670.         }
  671.     }

  672.     /**
  673.      * @since 2.5.0
  674.      */
  675.     @Override
  676.     public void setLargeMaxRows(final long max) throws SQLException {
  677.         checkOpen();
  678.         try {
  679.             statement.setLargeMaxRows(max);
  680.         } catch (final SQLException e) {
  681.             handleException(e);
  682.         }
  683.     }

  684.     private void setLastUsedInParent() {
  685.         if (connection != null) {
  686.             connection.setLastUsed();
  687.         }
  688.     }

  689.     @Override
  690.     public void setMaxFieldSize(final int max) throws SQLException {
  691.         checkOpen();
  692.         try {
  693.             statement.setMaxFieldSize(max);
  694.         } catch (final SQLException e) {
  695.             handleException(e);
  696.         }
  697.     }

  698.     @Override
  699.     public void setMaxRows(final int max) throws SQLException {
  700.         checkOpen();
  701.         try {
  702.             statement.setMaxRows(max);
  703.         } catch (final SQLException e) {
  704.             handleException(e);
  705.         }
  706.     }

  707.     @Override
  708.     public void setPoolable(final boolean poolable) throws SQLException {
  709.         checkOpen();
  710.         try {
  711.             statement.setPoolable(poolable);
  712.         } catch (final SQLException e) {
  713.             handleException(e);
  714.         }
  715.     }

  716.     @Override
  717.     public void setQueryTimeout(final int seconds) throws SQLException {
  718.         checkOpen();
  719.         try {
  720.             statement.setQueryTimeout(seconds);
  721.         } catch (final SQLException e) {
  722.             handleException(e);
  723.         }
  724.     }

  725.     /**
  726.      * Returns a String representation of this object.
  727.      *
  728.      * @return String
  729.      */
  730.     @Override
  731.     public synchronized String toString() {
  732.         return Objects.toString(statement, "NULL");
  733.     }

  734.     @Override
  735.     public <T> T unwrap(final Class<T> iface) throws SQLException {
  736.         if (iface.isAssignableFrom(getClass())) {
  737.             return iface.cast(this);
  738.         }
  739.         if (iface.isAssignableFrom(statement.getClass())) {
  740.             return iface.cast(statement);
  741.         }
  742.         return statement.unwrap(iface);
  743.     }
  744. }