View Javadoc
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  
19  import java.io.InputStream;
20  import java.io.Reader;
21  import java.math.BigDecimal;
22  import java.net.URL;
23  import java.sql.Array;
24  import java.sql.Blob;
25  import java.sql.Clob;
26  import java.sql.Connection;
27  import java.sql.DatabaseMetaData;
28  import java.sql.Date;
29  import java.sql.Ref;
30  import java.sql.ResultSet;
31  import java.sql.RowId;
32  import java.sql.SQLException;
33  import java.sql.SQLFeatureNotSupportedException;
34  import java.sql.SQLXML;
35  import java.sql.Statement;
36  import java.sql.Time;
37  import java.sql.Timestamp;
38  import java.util.concurrent.Executor;
39  import java.util.logging.Logger;
40  
41  import javax.sql.CommonDataSource;
42  
43  /**
44   * Defines bridge methods to JDBC 4.1 (Java 7 or above) methods to allow call sites to operate safely (without
45   * {@link AbstractMethodError}) when using a JDBC driver written for JDBC 4.0 (Java 6 or above).
46   * <p>
47   * 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.
48   * </p>
49   * <p>
50   * This should probably be moved or at least copied in some form to Apache Commons DbUtils.
51   * </p>
52   *
53   * @since 2.6.0
54   */
55  public class Jdbc41Bridge {
56  
57      /**
58       * Delegates to {@link Connection#abort(Executor)} without throwing an {@link AbstractMethodError}.
59       * <p>
60       * If the JDBC driver does not implement {@link Connection#abort(Executor)}, then call {@link Connection#close()}.
61       * </p>
62       *
63       * @param connection
64       *            the receiver
65       * @param executor
66       *            See {@link Connection#abort(Executor)}.
67       * @throws SQLException
68       *             See {@link Connection#abort(Executor)}.
69       * @see Connection#abort(Executor)
70       */
71      public static void abort(final Connection connection, final Executor executor) throws SQLException {
72          try {
73              connection.abort(executor);
74          } catch (final AbstractMethodError e) {
75              connection.close();
76          }
77      }
78  
79      /**
80       * Delegates to {@link Statement#closeOnCompletion()} without throwing an {@link AbstractMethodError}.
81       * <p>
82       * If the JDBC driver does not implement {@link Statement#closeOnCompletion()}, then just check that the connection
83       * is closed to then throw an SQLException.
84       * </p>
85       *
86       * @param statement
87       *            See {@link Statement#closeOnCompletion()}
88       * @throws SQLException
89       *             See {@link Statement#closeOnCompletion()}
90       * @see Statement#closeOnCompletion()
91       */
92      public static void closeOnCompletion(final Statement statement) throws SQLException {
93          try {
94              statement.closeOnCompletion();
95          } catch (final AbstractMethodError e) {
96              if (statement.isClosed()) {
97                  throw new SQLException("Statement closed");
98              }
99          }
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 }