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 (Exception e) {
231            return false;
232
233        }
234    }
235
236    /**
237     * Print the stack trace for a SQLException to STDERR.
238     *
239     * @param e SQLException to print stack trace of
240     */
241    public static void printStackTrace(SQLException e) {
242        printStackTrace(e, new PrintWriter(System.err));
243    }
244
245    /**
246     * Print the stack trace for a SQLException to a
247     * specified PrintWriter.
248     *
249     * @param e SQLException to print stack trace of
250     * @param pw PrintWriter to print to
251     */
252    public static void printStackTrace(SQLException e, PrintWriter pw) {
253
254        SQLException next = e;
255        while (next != null) {
256            next.printStackTrace(pw);
257            next = next.getNextException();
258            if (next != null) {
259                pw.println("Next SQLException:");
260            }
261        }
262    }
263
264    /**
265     * Print warnings on a Connection to STDERR.
266     *
267     * @param conn Connection to print warnings from
268     */
269    public static void printWarnings(Connection conn) {
270        printWarnings(conn, new PrintWriter(System.err));
271    }
272
273    /**
274     * Print warnings on a Connection to a specified PrintWriter.
275     *
276     * @param conn Connection to print warnings from
277     * @param pw PrintWriter to print to
278     */
279    public static void printWarnings(Connection conn, PrintWriter pw) {
280        if (conn != null) {
281            try {
282                printStackTrace(conn.getWarnings(), pw);
283            } catch (SQLException e) {
284                printStackTrace(e, pw);
285            }
286        }
287    }
288
289    /**
290     * Rollback any changes made on the given connection.
291     * @param conn Connection to rollback.  A null value is legal.
292     * @throws SQLException if a database access error occurs
293     */
294    public static void rollback(Connection conn) throws SQLException {
295        if (conn != null) {
296            conn.rollback();
297        }
298    }
299
300    /**
301     * Performs a rollback on the <code>Connection</code> then closes it,
302     * avoid closing if null.
303     *
304     * @param conn Connection to rollback.  A null value is legal.
305     * @throws SQLException if a database access error occurs
306     * @since DbUtils 1.1
307     */
308    public static void rollbackAndClose(Connection conn) throws SQLException {
309        if (conn != null) {
310            try {
311                conn.rollback();
312            } finally {
313                conn.close();
314            }
315        }
316    }
317
318    /**
319     * Performs a rollback on the <code>Connection</code> then closes it,
320     * avoid closing if null and hide any SQLExceptions that occur.
321     *
322     * @param conn Connection to rollback.  A null value is legal.
323     * @since DbUtils 1.1
324     */
325    public static void rollbackAndCloseQuietly(Connection conn) {
326        try {
327            rollbackAndClose(conn);
328        } catch (SQLException e) { // NOPMD
329            // quiet
330        }
331    }
332
333    /**
334     * Simple {@link Driver} proxy class that proxies a JDBC Driver loaded dynamically.
335     *
336     * @since 1.6
337     */
338    private static final class DriverProxy implements Driver {
339
340        private boolean parentLoggerSupported = true;
341
342        /**
343         * The adapted JDBC Driver loaded dynamically.
344         */
345        private final Driver adapted;
346
347        /**
348         * Creates a new JDBC Driver that adapts a JDBC Driver loaded dynamically.
349         *
350         * @param adapted the adapted JDBC Driver loaded dynamically.
351         */
352        public DriverProxy(Driver adapted) {
353            this.adapted = adapted;
354        }
355
356        /**
357         * {@inheritDoc}
358         */
359        @Override
360        public boolean acceptsURL(String url) throws SQLException {
361            return adapted.acceptsURL(url);
362        }
363
364        /**
365         * {@inheritDoc}
366         */
367        @Override
368        public Connection connect(String url, Properties info) throws SQLException {
369            return adapted.connect(url, info);
370        }
371
372        /**
373         * {@inheritDoc}
374         */
375        @Override
376        public int getMajorVersion() {
377            return adapted.getMajorVersion();
378        }
379
380        /**
381         * {@inheritDoc}
382         */
383        @Override
384        public int getMinorVersion() {
385            return adapted.getMinorVersion();
386        }
387
388        /**
389         * {@inheritDoc}
390         */
391        @Override
392        public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
393            return adapted.getPropertyInfo(url, info);
394        }
395
396        /**
397         * {@inheritDoc}
398         */
399        @Override
400        public boolean jdbcCompliant() {
401            return adapted.jdbcCompliant();
402        }
403
404        /**
405         * Java 1.7 method.
406         */
407        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
408            if (parentLoggerSupported) {
409                try {
410                    Method method = adapted.getClass().getMethod("getParentLogger", new Class[0]);
411                    return (Logger)method.invoke(adapted, new Object[0]);
412                } catch (NoSuchMethodException e) {
413                    parentLoggerSupported = false;
414                    throw new SQLFeatureNotSupportedException(e);
415                } catch (IllegalAccessException e) {
416                    parentLoggerSupported = false;
417                    throw new SQLFeatureNotSupportedException(e);
418                } catch (InvocationTargetException e) {
419                    parentLoggerSupported = false;
420                    throw new SQLFeatureNotSupportedException(e);
421                }
422            }
423            throw new SQLFeatureNotSupportedException();
424        }
425
426    }
427
428}