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=%s, 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}