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 or above) methods to allow call sites to operate safely (without
045 * {@link AbstractMethodError}) when using a JDBC driver written for JDBC 4.0 (Java 6 or above).
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}