    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.dbcp2;
    018
    019import java.io.InputStream;
    020import java.io.Reader;
    021import java.math.BigDecimal;
    022import java.net.URL;
    023import java.sql.Array;
    024import java.sql.Blob;
    025import java.sql.Clob;
    026import java.sql.Connection;
    027import java.sql.DatabaseMetaData;
    028import java.sql.Date;
    029import java.sql.Ref;
    030import java.sql.ResultSet;
    031import java.sql.RowId;
    032import java.sql.SQLException;
    033import java.sql.SQLFeatureNotSupportedException;
    034import java.sql.SQLXML;
    035import java.sql.Statement;
    036import java.sql.Time;
    037import java.sql.Timestamp;
    038import java.util.concurrent.Executor;
    039import java.util.logging.Logger;
    040
    041import javax.sql.CommonDataSource;
    042
    043/**
    044 * Defines bridge methods to JDBC 4.1 (Java 7) methods to allow call sites to operate safely (without
    045 * {@link AbstractMethodError}) when using a JDBC driver written for JDBC 4.0 (Java 6).
    046 *
    047 * @since 2.6.0
    048 */
    049public class Jdbc41Bridge {
    050
    051    /**
    052     * Delegates to {@link Connection#abort(Executor)} without throwing a {@link AbstractMethodError}.
    053     * <p>
    054     * If the JDBC driver does not implement {@link Connection#abort(Executor)}, then call {@link Connection#close()}.
    055     * </p>
    056     *
    057     * @param connection
    058     *            the receiver
    059     * @param executor
    060     *            See {@link Connection#abort(Executor)}.
    061     * @throws SQLException
    062     *             See {@link Connection#abort(Executor)}.
    063     * @see Connection#abort(Executor)
    064     */
    065    public static void abort(final Connection connection, final Executor executor) throws SQLException {
    066        try {
    067            connection.abort(executor);
    068        } catch (final AbstractMethodError e) {
    069            connection.close();
    070        }
    071    }
    072
    073    /**
    074     * Delegates to {@link DatabaseMetaData#generatedKeyAlwaysReturned()} without throwing a
    075     * {@link AbstractMethodError}.
    076     * <p>
    077     * If the JDBC driver does not implement {@link DatabaseMetaData#generatedKeyAlwaysReturned()}, then return false.
    078     * </p>
    079     *
    080     * @param databaseMetaData
    081     *            See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
    082     * @return See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
    083     * @throws SQLException
    084     *             See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
    085     * @see DatabaseMetaData#generatedKeyAlwaysReturned()
    086     */
    087    public static boolean generatedKeyAlwaysReturned(final DatabaseMetaData databaseMetaData) throws SQLException {
    088        try {
    089            return databaseMetaData.generatedKeyAlwaysReturned();
    090        } catch (final AbstractMethodError e) {
    091            // do nothing
    092            return false;
    093        }
    094    }
    095
    096    /**
    097     * Delegates to {@link Connection#getNetworkTimeout()} without throwing a {@link AbstractMethodError}.
    098     * <p>
    099     * If the JDBC driver does not implement {@link Connection#getNetworkTimeout()}, then return 0.
    100     * </p>
    101     *
    102     * @param connection
    103     *            the receiver
    104     * @return See {@link Connection#getNetworkTimeout()}
    105     * @throws SQLException
    106     *             See {@link Connection#getNetworkTimeout()}
    107     * @see Connection#getNetworkTimeout()
    108     */
    109    public static int getNetworkTimeout(final Connection connection) throws SQLException {
    110        try {
    111            return connection.getNetworkTimeout();
    112        } catch (final AbstractMethodError e) {
    113            return 0;
    114        }
    115    }
    116
    117    /**
    118     * Delegates to {@link ResultSet#getObject(int, Class)} without throwing a {@link AbstractMethodError}.
    119     * <p>
    120     * If the JDBC driver does not implement {@link ResultSet#getObject(int, Class)}, then return 0.
    121     * </p>
    122     *
    123     * @param <T>
    124     *            See {@link ResultSet#getObject(int, Class)}
    125     * @param resultSet
    126     *            See {@link ResultSet#getObject(int, Class)}
    127     * @param columnIndex
    128     *            See {@link ResultSet#getObject(int, Class)}
    129     * @param type
    130     *            See {@link ResultSet#getObject(int, Class)}
    131     * @return See {@link ResultSet#getObject(int, Class)}
    132     * @throws SQLException
    133     *             See {@link ResultSet#getObject(int, Class)}
    134     * @see ResultSet#getObject(int, Class)
    135     */
    136    @SuppressWarnings("unchecked")
    137    public static <T> T getObject(final ResultSet resultSet, final int columnIndex, final Class<T> type)
    138            throws SQLException {
    139        try {
    140            return resultSet.getObject(columnIndex, type);
    141        } catch (final AbstractMethodError e) {
    142            if (type == String.class) {
    143                return (T) resultSet.getString(columnIndex);
    144            }
    145            // Numbers
    146            if (type == Integer.class) {
    147                return (T) Integer.valueOf(resultSet.getInt(columnIndex));
    148            }
    149            if (type == Long.class) {
    150                return (T) Long.valueOf(resultSet.getLong(columnIndex));
    151            }
    152            if (type == Double.class) {
    153                return (T) Double.valueOf(resultSet.getDouble(columnIndex));
    154            }
    155            if (type == Float.class) {
    156                return (T) Float.valueOf(resultSet.getFloat(columnIndex));
    157            }
    158            if (type == Short.class) {
    159                return (T) Short.valueOf(resultSet.getShort(columnIndex));
    160            }
    161            if (type == BigDecimal.class) {
    162                return (T) resultSet.getBigDecimal(columnIndex);
    163            }
    164            if (type == Byte.class) {
    165                return (T) Byte.valueOf(resultSet.getByte(columnIndex));
    166            }
    167            // Dates
    168            if (type == Date.class) {
    169                return (T) resultSet.getDate(columnIndex);
    170            }
    171            if (type == Time.class) {
    172                return (T) resultSet.getTime(columnIndex);
    173            }
    174            if (type == Timestamp.class) {
    175                return (T) resultSet.getTimestamp(columnIndex);
    176            }
    177            // Streams
    178            if (type == InputStream.class) {
    179                return (T) resultSet.getBinaryStream(columnIndex);
    180            }
    181            if (type == Reader.class) {
    182                return (T) resultSet.getCharacterStream(columnIndex);
    183            }
    184            // Other
    185            if (type == Object.class) {
    186                return (T) resultSet.getObject(columnIndex);
    187            }
    188            if (type == Boolean.class) {
    189                return (T) Boolean.valueOf(resultSet.getBoolean(columnIndex));
    190            }
    191            if (type == Array.class) {
    192                return (T) resultSet.getArray(columnIndex);
    193            }
    194            if (type == Blob.class) {
    195                return (T) resultSet.getBlob(columnIndex);
    196            }
    197            if (type == Clob.class) {
    198                return (T) resultSet.getClob(columnIndex);
    199            }
    200            if (type == Ref.class) {
    201                return (T) resultSet.getRef(columnIndex);
    202            }
    203            if (type == RowId.class) {
    204                return (T) resultSet.getRowId(columnIndex);
    205            }
    206            if (type == SQLXML.class) {
    207                return (T) resultSet.getSQLXML(columnIndex);
    208            }
    209            if (type == URL.class) {
    210                return (T) resultSet.getURL(columnIndex);
    211            }
    212            throw new SQLFeatureNotSupportedException(
    213                    String.format("resultSet=%s, columnIndex=%,d, type=%s", resultSet, columnIndex, type));
    214        }
    215    }
    216
    217    /**
    218     * Delegates to {@link ResultSet#getObject(String, Class)} without throwing a {@link AbstractMethodError}.
    219     *
    220     * @param <T>
    221     *            See {@link ResultSet#getObject(String, Class)}
    222     * @param resultSet
    223     *            See {@link ResultSet#getObject(String, Class)}
    224     * @param columnLabel
    225     *            See {@link ResultSet#getObject(String, Class)}
    226     * @param type
    227     *            See {@link ResultSet#getObject(String, Class)}
    228     * @return See {@link ResultSet#getObject(String, Class)}
    229     * @throws SQLException
    230     *             See {@link ResultSet#getObject(String, Class)}
    231     * @see ResultSet#getObject(int, Class)
    232     */
    233    @SuppressWarnings("unchecked")
    234    public static <T> T getObject(final ResultSet resultSet, final String columnLabel, final Class<T> type)
    235            throws SQLException {
    236        try {
    237            return resultSet.getObject(columnLabel, type);
    238        } catch (final AbstractMethodError e) {
    239            // Numbers
    240            if (type == Integer.class) {
    241                return (T) Integer.valueOf(resultSet.getInt(columnLabel));
    242            }
    243            if (type == Long.class) {
    244                return (T) Long.valueOf(resultSet.getLong(columnLabel));
    245            }
    246            if (type == Double.class) {
    247                return (T) Double.valueOf(resultSet.getDouble(columnLabel));
    248            }
    249            if (type == Float.class) {
    250                return (T) Float.valueOf(resultSet.getFloat(columnLabel));
    251            }
    252            if (type == Short.class) {
    253                return (T) Short.valueOf(resultSet.getShort(columnLabel));
    254            }
    255            if (type == BigDecimal.class) {
    256                return (T) resultSet.getBigDecimal(columnLabel);
    257            }
    258            if (type == Byte.class) {
    259                return (T) Byte.valueOf(resultSet.getByte(columnLabel));
    260            }
    261            // Dates
    262            if (type == Date.class) {
    263                return (T) resultSet.getDate(columnLabel);
    264            }
    265            if (type == Time.class) {
    266                return (T) resultSet.getTime(columnLabel);
    267            }
    268            if (type == Timestamp.class) {
    269                return (T) resultSet.getTimestamp(columnLabel);
    270            }
    271            // Streams
    272            if (type == InputStream.class) {
    273                return (T) resultSet.getBinaryStream(columnLabel);
    274            }
    275            if (type == Reader.class) {
    276                return (T) resultSet.getCharacterStream(columnLabel);
    277            }
    278            // Other
    279            if (type == Object.class) {
    280                return (T) resultSet.getObject(columnLabel);
    281            }
    282            if (type == Boolean.class) {
    283                return (T) Boolean.valueOf(resultSet.getBoolean(columnLabel));
    284            }
    285            if (type == Array.class) {
    286                return (T) resultSet.getArray(columnLabel);
    287            }
    288            if (type == Blob.class) {
    289                return (T) resultSet.getBlob(columnLabel);
    290            }
    291            if (type == Clob.class) {
    292                return (T) resultSet.getClob(columnLabel);
    293            }
    294            if (type == Ref.class) {
    295                return (T) resultSet.getRef(columnLabel);
    296            }
    297            if (type == RowId.class) {
    298                return (T) resultSet.getRowId(columnLabel);
    299            }
    300            if (type == SQLXML.class) {
    301                return (T) resultSet.getSQLXML(columnLabel);
    302            }
    303            if (type == URL.class) {
    304                return (T) resultSet.getURL(columnLabel);
    305            }
    306            throw new SQLFeatureNotSupportedException(
    307                    String.format("resultSet=%s, columnLabel=%,d, type=%s", resultSet, columnLabel, type));
    308        }
    309    }
    310
    311    /**
    312     * Delegates to {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)} without throwing a
    313     * {@link AbstractMethodError}.
    314     * <p>
    315     * If the JDBC driver does not implement {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)},
    316     * then return null.
    317     * </p>
    318     *
    319     * @param databaseMetaData
    320     *            the receiver
    321     * @param catalog
    322     *            See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
    323     * @param schemaPattern
    324     *            See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
    325     * @param tableNamePattern
    326     *            See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
    327     * @param columnNamePattern
    328     *            See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
    329     * @return See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
    330     * @throws SQLException
    331     *             See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
    332     * @see DatabaseMetaData#getPseudoColumns(String, String, String, String)
    333     */
    334    public static ResultSet getPseudoColumns(final DatabaseMetaData databaseMetaData, final String catalog,
    335            final String schemaPattern, final String tableNamePattern, final String columnNamePattern)
    336            throws SQLException {
    337        try {
    338            return databaseMetaData.getPseudoColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern);
    339        } catch (final AbstractMethodError e) {
    340            // do nothing
    341            return null;
    342        }
    343    }
    344
    345    /**
    346     * Delegates to {@link Connection#getSchema()} without throwing a {@link AbstractMethodError}.
    347     * <p>
    348     * If the JDBC driver does not implement {@link Connection#getSchema()}, then return null.
    349     * </p>
    350     *
    351     * @param connection
    352     *            the receiver
    353     * @return null for a JDBC 4 driver or a value per {@link Connection#getSchema()}.
    354     * @throws SQLException
    355     *             See {@link Connection#getSchema()}.
    356     * @see Connection#getSchema()
    357     */
    358    public static String getSchema(final Connection connection) throws SQLException {
    359        try {
    360            return connection.getSchema();
    361        } catch (final AbstractMethodError e) {
    362            // do nothing
    363            return null;
    364        }
    365    }
    366
    367    /**
    368     * Delegates to {@link Connection#setNetworkTimeout(Executor, int)} without throwing a {@link AbstractMethodError}.
    369     * <p>
    370     * If the JDBC driver does not implement {@link Connection#setNetworkTimeout(Executor, int)}, then do nothing.
    371     * </p>
    372     *
    373     * @param connection
    374     *            the receiver
    375     * @param executor
    376     *            See {@link Connection#setNetworkTimeout(Executor, int)}
    377     * @param milliseconds
    378     *            {@link Connection#setNetworkTimeout(Executor, int)}
    379     * @throws SQLException
    380     *             {@link Connection#setNetworkTimeout(Executor, int)}
    381     * @see Connection#setNetworkTimeout(Executor, int)
    382     */
    383    public static void setNetworkTimeout(final Connection connection, final Executor executor, final int milliseconds)
    384            throws SQLException {
    385        try {
    386            connection.setNetworkTimeout(executor, milliseconds);
    387        } catch (final AbstractMethodError e) {
    388            // do nothing
    389        }
    390    }
    391
    392    /**
    393     * Delegates to {@link Connection#setSchema(String)} without throwing a {@link AbstractMethodError}.
    394     * <p>
    395     * If the JDBC driver does not implement {@link Connection#setSchema(String)}, then do nothing.
    396     * </p>
    397     *
    398     * @param connection
    399     *            the receiver
    400     * @param schema
    401     *            See {@link Connection#setSchema(String)}.
    402     * @throws SQLException
    403     *             See {@link Connection#setSchema(String)}.
    404     * @see Connection#setSchema(String)
    405     */
    406    public static void setSchema(final Connection connection, final String schema) throws SQLException {
    407        try {
    408            connection.setSchema(schema);
    409        } catch (final AbstractMethodError e) {
    410            // do nothing
    411        }
    412    }
    413
    414    /**
    415     * Delegates to {@link Statement#closeOnCompletion()} without throwing a {@link AbstractMethodError}.
    416     * <p>
    417     * If the JDBC driver does not implement {@link Statement#closeOnCompletion()}, then just check that the connection
    418     * is closed to then throw a SQLException.
    419     * </p>
    420     *
    421     * @param statement
    422     *            See {@link Statement#closeOnCompletion()}
    423     * @throws SQLException
    424     *             See {@link Statement#closeOnCompletion()}
    425     * @see Statement#closeOnCompletion()
    426     */
    427    public static void closeOnCompletion(final Statement statement) throws SQLException {
    428        try {
    429            statement.closeOnCompletion();
    430        } catch (final AbstractMethodError e) {
    431            if (statement.isClosed()) {
    432                throw new SQLException("Statement closed");
    433            }
    434        }
    435    }
    436
    437    /**
    438     * Delegates to {@link Statement#isCloseOnCompletion()} without throwing a {@link AbstractMethodError}.
    439     * <p>
    440     * If the JDBC driver does not implement {@link Statement#isCloseOnCompletion()}, then just check that the
    441     * connection is closed to then throw a SQLException.
    442     * </p>
    443     *
    444     * @param statement
    445     *            See {@link Statement#isCloseOnCompletion()}
    446     * @return See {@link Statement#isCloseOnCompletion()}
    447     * @throws SQLException
    448     *             See {@link Statement#isCloseOnCompletion()}
    449     * @see Statement#closeOnCompletion()
    450     */
    451    public static boolean isCloseOnCompletion(final Statement statement) throws SQLException {
    452        try {
    453            return statement.isCloseOnCompletion();
    454        } catch (final AbstractMethodError e) {
    455            if (statement.isClosed()) {
    456                throw new SQLException("Statement closed");
    457            }
    458            return false;
    459        }
    460    }
    461
    462    /**
    463     * Delegates to {@link CommonDataSource#getParentLogger()} without throwing a {@link AbstractMethodError}.
    464     * <p>
    465     * If the JDBC driver does not implement {@link CommonDataSource#getParentLogger()}, then return null.
    466     * </p>
    467     *
    468     * @param commonDataSource
    469     *            See {@link CommonDataSource#getParentLogger()}
    470     * @return See {@link CommonDataSource#getParentLogger()}
    471     * @throws SQLFeatureNotSupportedException
    472     *             See {@link CommonDataSource#getParentLogger()}
    473     */
    474    public static Logger getParentLogger(final CommonDataSource commonDataSource) throws SQLFeatureNotSupportedException {
    475        try {
    476            return commonDataSource.getParentLogger();
    477        } catch (final AbstractMethodError e) {
    478            throw new SQLFeatureNotSupportedException("javax.sql.CommonDataSource#getParentLogger()");
    479        }
    480    }
    481
    482}