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.dbutils;
018
019import static java.sql.DriverManager.registerDriver;
020
021import java.io.PrintWriter;
022import java.lang.reflect.Constructor;
023import java.lang.reflect.InvocationTargetException;
024import java.lang.reflect.Method;
025import java.sql.Connection;
026import java.sql.Driver;
027import java.sql.DriverPropertyInfo;
028import java.sql.ResultSet;
029import java.sql.SQLException;
030import java.sql.SQLFeatureNotSupportedException;
031import java.sql.Statement;
032import java.util.logging.Logger;
033import java.util.Properties;
034
035/**
036 * A collection of JDBC helper methods.  This class is thread safe.
037 */
038public final class DbUtils {
039
040    /**
041     * Default constructor.
042     *
043     * Utility classes should not have a public or default constructor,
044     * but this one preserves retro-compatibility.
045     *
046     * @since 1.4
047     */
048    public DbUtils() {
049        // do nothing
050    }
051
052    /**
053     * Close a <code>Connection</code>, avoid closing if null.
054     *
055     * @param conn Connection to close.
056     * @throws SQLException if a database access error occurs
057     */
058    public static void close(Connection conn) throws SQLException {
059        if (conn != null) {
060            conn.close();
061        }
062    }
063
064    /**
065     * Close a <code>ResultSet</code>, avoid closing if null.
066     *
067     * @param rs ResultSet to close.
068     * @throws SQLException if a database access error occurs
069     */
070    public static void close(ResultSet rs) throws SQLException {
071        if (rs != null) {
072            rs.close();
073        }
074    }
075
076    /**
077     * Close a <code>Statement</code>, avoid closing if null.
078     *
079     * @param stmt Statement to close.
080     * @throws SQLException if a database access error occurs
081     */
082    public static void close(Statement stmt) throws SQLException {
083        if (stmt != null) {
084            stmt.close();
085        }
086    }
087
088    /**
089     * Close a <code>Connection</code>, avoid closing if null and hide
090     * any SQLExceptions that occur.
091     *
092     * @param conn Connection to close.
093     */
094    public static void closeQuietly(Connection conn) {
095        try {
096            close(conn);
097        } catch (SQLException e) { // NOPMD
098            // quiet
099        }
100    }
101
102    /**
103     * Close a <code>Connection</code>, <code>Statement</code> and
104     * <code>ResultSet</code>.  Avoid closing if null and hide any
105     * SQLExceptions that occur.
106     *
107     * @param conn Connection to close.
108     * @param stmt Statement to close.
109     * @param rs ResultSet to close.
110     */
111    public static void closeQuietly(Connection conn, Statement stmt,
112            ResultSet rs) {
113
114        try {
115            closeQuietly(rs);
116        } finally {
117            try {
118                closeQuietly(stmt);
119            } finally {
120                closeQuietly(conn);
121            }
122        }
123
124    }
125
126    /**
127     * Close a <code>ResultSet</code>, avoid closing if null and hide any
128     * SQLExceptions that occur.
129     *
130     * @param rs ResultSet to close.
131     */
132    public static void closeQuietly(ResultSet rs) {
133        try {
134            close(rs);
135        } catch (SQLException e) { // NOPMD
136            // quiet
137        }
138    }
139
140    /**
141     * Close a <code>Statement</code>, avoid closing if null and hide
142     * any SQLExceptions that occur.
143     *
144     * @param stmt Statement to close.
145     */
146    public static void closeQuietly(Statement stmt) {
147        try {
148            close(stmt);
149        } catch (SQLException e) { // NOPMD
150            // quiet
151        }
152    }
153
154    /**
155     * Commits a <code>Connection</code> then closes it, avoid closing if null.
156     *
157     * @param conn Connection to close.
158     * @throws SQLException if a database access error occurs
159     */
160    public static void commitAndClose(Connection conn) throws SQLException {
161        if (conn != null) {
162            try {
163                conn.commit();
164            } finally {
165                conn.close();
166            }
167        }
168    }
169
170    /**
171     * Commits a <code>Connection</code> then closes it, avoid closing if null
172     * and hide any SQLExceptions that occur.
173     *
174     * @param conn Connection to close.
175     */
176    public static void commitAndCloseQuietly(Connection conn) {
177        try {
178            commitAndClose(conn);
179        } catch (SQLException e) { // NOPMD
180            // quiet
181        }
182    }
183
184    /**
185     * Loads and registers a database driver class.
186     * If this succeeds, it returns true, else it returns false.
187     *
188     * @param driverClassName of driver to load
189     * @return boolean <code>true</code> if the driver was found, otherwise <code>false</code>
190     */
191    public static boolean loadDriver(String driverClassName) {
192        return loadDriver(DbUtils.class.getClassLoader(), driverClassName);
193    }
194
195    /**
196     * Loads and registers a database driver class.
197     * If this succeeds, it returns true, else it returns false.
198     *
199     * @param classLoader the class loader used to load the driver class
200     * @param driverClassName of driver to load
201     * @return boolean <code>true</code> if the driver was found, otherwise <code>false</code>
202     * @since 1.4
203     */
204    public static boolean loadDriver(ClassLoader classLoader, String driverClassName) {
205        try {
206            Class<?> loadedClass = classLoader.loadClass(driverClassName);
207
208            if (!Driver.class.isAssignableFrom(loadedClass)) {
209                return false;
210            }
211
212            @SuppressWarnings("unchecked") // guarded by previous check
213            Class<Driver> driverClass = (Class<Driver>) loadedClass;
214            Constructor<Driver> driverConstructor = driverClass.getConstructor();
215
216            // make Constructor accessible if it is private
217            boolean isConstructorAccessible = driverConstructor.isAccessible();
218            if (!isConstructorAccessible) {
219                driverConstructor.setAccessible(true);
220            }
221
222            try {
223                Driver driver = driverConstructor.newInstance();
224                registerDriver(new DriverProxy(driver));
225            } finally {
226                driverConstructor.setAccessible(isConstructorAccessible);
227            }
228
229            return true;
230        } catch (RuntimeException e) {
231            return false;
232        } catch (Exception e) {
233            return false;
234        }
235    }
236
237    /**
238     * Print the stack trace for a SQLException to STDERR.
239     *
240     * @param e SQLException to print stack trace of
241     */
242    public static void printStackTrace(SQLException e) {
243        printStackTrace(e, new PrintWriter(System.err));
244    }
245
246    /**
247     * Print the stack trace for a SQLException to a
248     * specified PrintWriter.
249     *
250     * @param e SQLException to print stack trace of
251     * @param pw PrintWriter to print to
252     */
253    public static void printStackTrace(SQLException e, PrintWriter pw) {
254
255        SQLException next = e;
256        while (next != null) {
257            next.printStackTrace(pw);
258            next = next.getNextException();
259            if (next != null) {
260                pw.println("Next SQLException:");
261            }
262        }
263    }
264
265    /**
266     * Print warnings on a Connection to STDERR.
267     *
268     * @param conn Connection to print warnings from
269     */
270    public static void printWarnings(Connection conn) {
271        printWarnings(conn, new PrintWriter(System.err));
272    }
273
274    /**
275     * Print warnings on a Connection to a specified PrintWriter.
276     *
277     * @param conn Connection to print warnings from
278     * @param pw PrintWriter to print to
279     */
280    public static void printWarnings(Connection conn, PrintWriter pw) {
281        if (conn != null) {
282            try {
283                printStackTrace(conn.getWarnings(), pw);
284            } catch (SQLException e) {
285                printStackTrace(e, pw);
286            }
287        }
288    }
289
290    /**
291     * Rollback any changes made on the given connection.
292     * @param conn Connection to rollback.  A null value is legal.
293     * @throws SQLException if a database access error occurs
294     */
295    public static void rollback(Connection conn) throws SQLException {
296        if (conn != null) {
297            conn.rollback();
298        }
299    }
300
301    /**
302     * Performs a rollback on the <code>Connection</code> then closes it,
303     * avoid closing if null.
304     *
305     * @param conn Connection to rollback.  A null value is legal.
306     * @throws SQLException if a database access error occurs
307     * @since DbUtils 1.1
308     */
309    public static void rollbackAndClose(Connection conn) throws SQLException {
310        if (conn != null) {
311            try {
312                conn.rollback();
313            } finally {
314                conn.close();
315            }
316        }
317    }
318
319    /**
320     * Performs a rollback on the <code>Connection</code> then closes it,
321     * avoid closing if null and hide any SQLExceptions that occur.
322     *
323     * @param conn Connection to rollback.  A null value is legal.
324     * @since DbUtils 1.1
325     */
326    public static void rollbackAndCloseQuietly(Connection conn) {
327        try {
328            rollbackAndClose(conn);
329        } catch (SQLException e) { // NOPMD
330            // quiet
331        }
332    }
333
334    /**
335     * Simple {@link Driver} proxy class that proxies a JDBC Driver loaded dynamically.
336     *
337     * @since 1.6
338     */
339    private static final class DriverProxy implements Driver {
340
341        private boolean parentLoggerSupported = true;
342
343        /**
344         * The adapted JDBC Driver loaded dynamically.
345         */
346        private final Driver adapted;
347
348        /**
349         * Creates a new JDBC Driver that adapts a JDBC Driver loaded dynamically.
350         *
351         * @param adapted the adapted JDBC Driver loaded dynamically.
352         */
353        public DriverProxy(Driver adapted) {
354            this.adapted = adapted;
355        }
356
357        /**
358         * {@inheritDoc}
359         */
360        @Override
361        public boolean acceptsURL(String url) throws SQLException {
362            return adapted.acceptsURL(url);
363        }
364
365        /**
366         * {@inheritDoc}
367         */
368        @Override
369        public Connection connect(String url, Properties info) throws SQLException {
370            return adapted.connect(url, info);
371        }
372
373        /**
374         * {@inheritDoc}
375         */
376        @Override
377        public int getMajorVersion() {
378            return adapted.getMajorVersion();
379        }
380
381        /**
382         * {@inheritDoc}
383         */
384        @Override
385        public int getMinorVersion() {
386            return adapted.getMinorVersion();
387        }
388
389        /**
390         * {@inheritDoc}
391         */
392        @Override
393        public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
394            return adapted.getPropertyInfo(url, info);
395        }
396
397        /**
398         * {@inheritDoc}
399         */
400        @Override
401        public boolean jdbcCompliant() {
402            return adapted.jdbcCompliant();
403        }
404
405        /**
406         * Java 1.7 method.
407         */
408        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
409            if (parentLoggerSupported) {
410                try {
411                    Method method = adapted.getClass().getMethod("getParentLogger", new Class[0]);
412                    return (Logger)method.invoke(adapted, new Object[0]);
413                } catch (NoSuchMethodException e) {
414                    parentLoggerSupported = false;
415                    throw new SQLFeatureNotSupportedException(e);
416                } catch (IllegalAccessException e) {
417                    parentLoggerSupported = false;
418                    throw new SQLFeatureNotSupportedException(e);
419                } catch (InvocationTargetException e) {
420                    parentLoggerSupported = false;
421                    throw new SQLFeatureNotSupportedException(e);
422                }
423            }
424            throw new SQLFeatureNotSupportedException();
425        }
426
427    }
428
429}