    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 * <p>
    047 * There should be no need to this kind of code for JDBC 4.2 in Java 8 due to JDBC's use of default methods.
    048 * </p>
    049 * <p>
    050 * This should probably be moved or at least copied in some form to Apache Commons DbUtils.
    051 * </p>
    052 *
    053 * @since 2.6.0
    054 */
    055public class Jdbc41Bridge {
    056
    057    /**
    058     * Delegates to {@link Connection#abort(Executor)} without throwing an {@link AbstractMethodError}.
    059     * <p>
    060     * If the JDBC driver does not implement {@link Connection#abort(Executor)}, then call {@link Connection#close()}.
    061     * </p>
    062     *
    063     * @param connection
    064     *            the receiver
    065     * @param executor
    066     *            See {@link Connection#abort(Executor)}.
    067     * @throws SQLException
    068     *             See {@link Connection#abort(Executor)}.
    069     * @see Connection#abort(Executor)
    070     */
    071    public static void abort(final Connection connection, final Executor executor) throws SQLException {
    072        try {
    073            connection.abort(executor);
    074        } catch (final AbstractMethodError e) {
    075            connection.close();
    076        }
    077    }
    078
    079    /**
    080     * Delegates to {@link Statement#closeOnCompletion()} without throwing an {@link AbstractMethodError}.
    081     * <p>
    082     * If the JDBC driver does not implement {@link Statement#closeOnCompletion()}, then just check that the connection
    083     * is closed to then throw an SQLException.
    084     * </p>
    085     *
    086     * @param statement
    087     *            See {@link Statement#closeOnCompletion()}
    088     * @throws SQLException
    089     *             See {@link Statement#closeOnCompletion()}
    090     * @see Statement#closeOnCompletion()
    091     */
    092    public static void closeOnCompletion(final Statement statement) throws SQLException {
    093        try {
    094            statement.closeOnCompletion();
    095        } catch (final AbstractMethodError e) {
    096            if (statement.isClosed()) {
    097                throw new SQLException("Statement closed");
    098            }
    099        }
    100    }
    101
    102    /**
    103     * Delegates to {@link DatabaseMetaData#generatedKeyAlwaysReturned()} without throwing a
    104     * {@link AbstractMethodError}.
    105     * <p>
    106     * If the JDBC driver does not implement {@link DatabaseMetaData#generatedKeyAlwaysReturned()}, then return false.
    107     * </p>
    108     *
    109     * @param databaseMetaData
    110     *            See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
    111     * @return See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
    112     * @throws SQLException
    113     *             See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
    114     * @see DatabaseMetaData#generatedKeyAlwaysReturned()
    115     */
    116    public static boolean generatedKeyAlwaysReturned(final DatabaseMetaData databaseMetaData) throws SQLException {
    117        try {
    118            return databaseMetaData.generatedKeyAlwaysReturned();
    119        } catch (final AbstractMethodError e) {
    120            // do nothing
    121            return false;
    122        }
    123    }
    124
    125    /**
    126     * Delegates to {@link Connection#getNetworkTimeout()} without throwing an {@link AbstractMethodError}.
    127     * <p>
    128     * If the JDBC driver does not implement {@link Connection#getNetworkTimeout()}, then return 0.
    129     * </p>
    130     *
    131     * @param connection
    132     *            the receiver
    133     * @return See {@link Connection#getNetworkTimeout()}
    134     * @throws SQLException
    135     *             See {@link Connection#getNetworkTimeout()}
    136     * @see Connection#getNetworkTimeout()
    137     */
    138    public static int getNetworkTimeout(final Connection connection) throws SQLException {
    139        try {
    140            return connection.getNetworkTimeout();
    141        } catch (final AbstractMethodError e) {
    142            return 0;
    143        }
    144    }
    145
    146    /**
    147     * Delegates to {@link ResultSet#getObject(int, Class)} without throwing an {@link AbstractMethodError}.
    148     * <p>
    149     * If the JDBC driver does not implement {@link ResultSet#getObject(int, Class)}, then return 0.
    150     * </p>
    151     *
    152     * @param <T>
    153     *            See {@link ResultSet#getObject(int, Class)}
    154     * @param resultSet
    155     *            See {@link ResultSet#getObject(int, Class)}
    156     * @param columnIndex
    157     *            See {@link ResultSet#getObject(int, Class)}
    158     * @param type
    159     *            See {@link ResultSet#getObject(int, Class)}
    160     * @return See {@link ResultSet#getObject(int, Class)}
    161     * @throws SQLException
    162     *             See {@link ResultSet#getObject(int, Class)}
    163     * @see ResultSet#getObject(int, Class)
    164     */
    165    @SuppressWarnings("unchecked")
    166    public static <T> T getObject(final ResultSet resultSet, final int columnIndex, final Class<T> type)
    167            throws SQLException {
    168        try {
    169            return resultSet.getObject(columnIndex, type);
    170        } catch (final AbstractMethodError e) {
    171            if (type == String.class) {
    172                return (T) resultSet.getString(columnIndex);
    173            }
    174            // Numbers
    175            if (type == Integer.class) {
    176                return (T) Integer.valueOf(resultSet.getInt(columnIndex));
    177            }
    178            if (type == Long.class) {
    179                return (T) Long.valueOf(resultSet.getLong(columnIndex));
    180            }
    181            if (type == Double.class) {
    182                return (T) Double.valueOf(resultSet.getDouble(columnIndex));
    183            }
    184            if (type == Float.class) {
    185                return (T) Float.valueOf(resultSet.getFloat(columnIndex));
    186            }
    187            if (type == Short.class) {
    188                return (T) Short.valueOf(resultSet.getShort(columnIndex));
    189            }
    190            if (type == BigDecimal.class) {
    191                return (T) resultSet.getBigDecimal(columnIndex);
    192            }
    193            if (type == Byte.class) {
    194                return (T) Byte.valueOf(resultSet.getByte(columnIndex));
    195            }
    196            // Dates
    197            if (type == Date.class) {
    198                return (T) resultSet.getDate(columnIndex);
    199            }
    200            if (type == Time.class) {
    201                return (T) resultSet.getTime(columnIndex);
    202            }
    203            if (type == Timestamp.class) {
    204                return (T) resultSet.getTimestamp(columnIndex);
    205            }
    206            // Streams
    207            if (type == InputStream.class) {
    208                return (T) resultSet.getBinaryStream(columnIndex);
    209            }
    210            if (type == Reader.class) {
    211                return (T) resultSet.getCharacterStream(columnIndex);
    212            }
    213            // Other
    214            if (type == Object.class) {
    215                return (T) resultSet.getObject(columnIndex);
    216            }
    217            if (type == Boolean.class) {
    218                return (T) Boolean.valueOf(resultSet.getBoolean(columnIndex));
    219            }
    220            if (type == Array.class) {
    221                return (T) resultSet.getArray(columnIndex);
    222            }
    223            if (type == Blob.class) {
    224                return (T) resultSet.getBlob(columnIndex);
    225            }
    226            if (type == Clob.class) {
    227                return (T) resultSet.getClob(columnIndex);
    228            }
    229            if (type == Ref.class) {
    230                return (T) resultSet.getRef(columnIndex);
    231            }
    232            if (type == RowId.class) {
    233                return (T) resultSet.getRowId(columnIndex);
    234            }
    235            if (type == SQLXML.class) {
    236                return (T) resultSet.getSQLXML(columnIndex);
    237            }
    238            if (type == URL.class) {
    239                return (T) resultSet.getURL(columnIndex);
    240            }
    241            throw new SQLFeatureNotSupportedException(
    242                    String.format("resultSet=%s, columnIndex=%,d, type=%s", resultSet, columnIndex, type));
    243        }
    244    }
    245
    246    /**
    247     * Delegates to {@link ResultSet#getObject(String, Class)} without throwing an {@link AbstractMethodError}.
    248     *
    249     * @param <T>
    250     *            See {@link ResultSet#getObject(String, Class)}
    251     * @param resultSet
    252     *            See {@link ResultSet#getObject(String, Class)}
    253     * @param columnLabel
    254     *            See {@link ResultSet#getObject(String, Class)}
    255     * @param type
    256     *            See {@link ResultSet#getObject(String, Class)}
    257     * @return See {@link ResultSet#getObject(String, Class)}
    258     * @throws SQLException
    259     *             See {@link ResultSet#getObject(String, Class)}
    260     * @see ResultSet#getObject(int, Class)
    261     */
    262    @SuppressWarnings("unchecked")
    263    public static <T> T getObject(final ResultSet resultSet, final String columnLabel, final Class<T> type)
    264            throws SQLException {
    265        try {
    266            return resultSet.getObject(columnLabel, type);
    267        } catch (final AbstractMethodError e) {
    268            // Numbers
    269            if (type == Integer.class) {
    270                return (T) Integer.valueOf(resultSet.getInt(columnLabel));
    271            }
    272            if (type == Long.class) {
    273                return (T) Long.valueOf(resultSet.getLong(columnLabel));
    274            }
    275            if (type == Double.class) {
    276                return (T) Double.valueOf(resultSet.getDouble(columnLabel));
    277            }
    278            if (type == Float.class) {
    279                return (T) Float.valueOf(resultSet.getFloat(columnLabel));
    280            }
    281            if (type == Short.class) {
    282                return (T) Short.valueOf(resultSet.getShort(columnLabel));
    283            }
    284            if (type == BigDecimal.class) {
    285                return (T) resultSet.getBigDecimal(columnLabel);
    286            }
    287            if (type == Byte.class) {
    288                return (T) Byte.valueOf(resultSet.getByte(columnLabel));
    289            }
    290            // Dates
    291            if (type == Date.class) {
    292                return (T) resultSet.getDate(columnLabel);
    293            }
    294            if (type == Time.class) {
    295                return (T) resultSet.getTime(columnLabel);
    296            }
    297            if (type == Timestamp.class) {
    298                return (T) resultSet.getTimestamp(columnLabel);
    299            }
    300            // Streams
    301            if (type == InputStream.class) {
    302                return (T) resultSet.getBinaryStream(columnLabel);
    303            }
    304            if (type == Reader.class) {
    305                return (T) resultSet.getCharacterStream(columnLabel);
    306            }
    307            // Other
    308            if (type == Object.class) {
    309                return (T) resultSet.getObject(columnLabel);
    310            }
    311            if (type == Boolean.class) {
    312                return (T) Boolean.valueOf(resultSet.getBoolean(columnLabel));
    313            }
    314            if (type == Array.class) {
    315                return (T) resultSet.getArray(columnLabel);
    316            }
    317            if (type == Blob.class) {
    318                return (T) resultSet.getBlob(columnLabel);
    319            }
    320            if (type == Clob.class) {
    321                return (T) resultSet.getClob(columnLabel);
    322            }
    323            if (type == Ref.class) {
    324                return (T) resultSet.getRef(columnLabel);
    325            }
    326            if (type == RowId.class) {
    327                return (T) resultSet.getRowId(columnLabel);
    328            }
    329            if (type == SQLXML.class) {
    330                return (T) resultSet.getSQLXML(columnLabel);
    331            }
    332            if (type == URL.class) {
    333                return (T) resultSet.getURL(columnLabel);
    334            }
    335            throw new SQLFeatureNotSupportedException(
    336                    String.format("resultSet=%s, columnLabel=%s, type=%s", resultSet, columnLabel, type));
    337        }
    338    }
    339
    340    /**
    341     * Delegates to {@link CommonDataSource#getParentLogger()} without throwing an {@link AbstractMethodError}.
    342     * <p>
    343     * If the JDBC driver does not implement {@link CommonDataSource#getParentLogger()}, then return null.
    344     * </p>
    345     *
    346     * @param commonDataSource
    347     *            See {@link CommonDataSource#getParentLogger()}
    348     * @return See {@link CommonDataSource#getParentLogger()}
    349     * @throws SQLFeatureNotSupportedException
    350     *             See {@link CommonDataSource#getParentLogger()}
    351     */
    352    public static Logger getParentLogger(final CommonDataSource commonDataSource) throws SQLFeatureNotSupportedException {
    353        try {
    354            return commonDataSource.getParentLogger();
    355        } catch (final AbstractMethodError e) {
    356            throw new SQLFeatureNotSupportedException("javax.sql.CommonDataSource#getParentLogger()");
    357        }
    358    }
    359
    360    /**
    361     * Delegates to {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)} without throwing a
    362     * {@link AbstractMethodError}.
    363     * <p>
    364     * If the JDBC driver does not implement {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)},
    365     * then return null.
    366     * </p>
    367     *
    368     * @param databaseMetaData
    369     *            the receiver
    370     * @param catalog
    371     *            See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
    372     * @param schemaPattern
    373     *            See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
    374     * @param tableNamePattern
    375     *            See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
    376     * @param columnNamePattern
    377     *            See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
    378     * @return See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
    379     * @throws SQLException
    380     *             See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
    381     * @see DatabaseMetaData#getPseudoColumns(String, String, String, String)
    382     */
    383    public static ResultSet getPseudoColumns(final DatabaseMetaData databaseMetaData, final String catalog,
    384            final String schemaPattern, final String tableNamePattern, final String columnNamePattern)
    385            throws SQLException {
    386        try {
    387            return databaseMetaData.getPseudoColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern);
    388        } catch (final AbstractMethodError e) {
    389            // do nothing
    390            return null;
    391        }
    392    }
    393
    394    /**
    395     * Delegates to {@link Connection#getSchema()} without throwing an {@link AbstractMethodError}.
    396     * <p>
    397     * If the JDBC driver does not implement {@link Connection#getSchema()}, then return null.
    398     * </p>
    399     *
    400     * @param connection
    401     *            the receiver
    402     * @return null for a JDBC 4 driver or a value per {@link Connection#getSchema()}.
    403     * @throws SQLException
    404     *             See {@link Connection#getSchema()}.
    405     * @see Connection#getSchema()
    406     */
    407    public static String getSchema(final Connection connection) throws SQLException {
    408        try {
    409            return connection.getSchema();
    410        } catch (final AbstractMethodError e) {
    411            // do nothing
    412            return null;
    413        }
    414    }
    415
    416    /**
    417     * Delegates to {@link Statement#isCloseOnCompletion()} without throwing an {@link AbstractMethodError}.
    418     * <p>
    419     * If the JDBC driver does not implement {@link Statement#isCloseOnCompletion()}, then just check that the
    420     * connection is closed to then throw an SQLException.
    421     * </p>
    422     *
    423     * @param statement
    424     *            See {@link Statement#isCloseOnCompletion()}
    425     * @return See {@link Statement#isCloseOnCompletion()}
    426     * @throws SQLException
    427     *             See {@link Statement#isCloseOnCompletion()}
    428     * @see Statement#closeOnCompletion()
    429     */
    430    public static boolean isCloseOnCompletion(final Statement statement) throws SQLException {
    431        try {
    432            return statement.isCloseOnCompletion();
    433        } catch (final AbstractMethodError e) {
    434            if (statement.isClosed()) {
    435                throw new SQLException("Statement closed");
    436            }
    437            return false;
    438        }
    439    }
    440
    441    /**
    442     * Delegates to {@link Connection#setNetworkTimeout(Executor, int)} without throwing an {@link AbstractMethodError}.
    443     * <p>
    444     * If the JDBC driver does not implement {@link Connection#setNetworkTimeout(Executor, int)}, then do nothing.
    445     * </p>
    446     *
    447     * @param connection
    448     *            the receiver
    449     * @param executor
    450     *            See {@link Connection#setNetworkTimeout(Executor, int)}
    451     * @param milliseconds
    452     *            {@link Connection#setNetworkTimeout(Executor, int)}
    453     * @throws SQLException
    454     *             {@link Connection#setNetworkTimeout(Executor, int)}
    455     * @see Connection#setNetworkTimeout(Executor, int)
    456     */
    457    public static void setNetworkTimeout(final Connection connection, final Executor executor, final int milliseconds)
    458            throws SQLException {
    459        try {
    460            connection.setNetworkTimeout(executor, milliseconds);
    461        } catch (final AbstractMethodError ignored) {
    462            // do nothing
    463        }
    464    }
    465
    466    /**
    467     * Delegates to {@link Connection#setSchema(String)} without throwing an {@link AbstractMethodError}.
    468     * <p>
    469     * If the JDBC driver does not implement {@link Connection#setSchema(String)}, then do nothing.
    470     * </p>
    471     *
    472     * @param connection
    473     *            the receiver
    474     * @param schema
    475     *            See {@link Connection#setSchema(String)}.
    476     * @throws SQLException
    477     *             See {@link Connection#setSchema(String)}.
    478     * @see Connection#setSchema(String)
    479     */
    480    public static void setSchema(final Connection connection, final String schema) throws SQLException {
    481        try {
    482            connection.setSchema(schema);
    483        } catch (final AbstractMethodError ignored) {
    484            // do nothing
    485        }
    486    }
    487
    488}