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