001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.oozie.util.db;
020
021import org.apache.commons.dbcp.BasicDataSource;
022import org.apache.commons.dbcp.ConnectionFactory;
023import org.apache.commons.dbcp.DriverConnectionFactory;
024import org.apache.commons.dbcp.SQLNestedException;
025
026import java.sql.Driver;
027import java.sql.DriverManager;
028import java.sql.SQLException;
029
030public class BasicDataSourceWrapper extends BasicDataSource {
031
032    /**
033     * Fixing a bug within {@link BasicDataSource#createConnectionFactory()} for {@code driverClassName} to have real effect.
034     * <p>
035     * Because we use currently Apache Commons DBCP 1.4.0 that has a bug not considering {@code driverClassName}, thus, we're unable
036     * to create a JDBC driver using a user-provided driver class name (we try to do that by setting explicitly a value for
037     * {@code openJpa.connectionProperties="DriverClassName=..."}), unless we perform the exact same fix that is applied by the DBCP
038     * patch.
039     * <p>
040     * Note: when DBCP 1.4.1 will be released, and Oozie will update to 1.4.1, we can remove this class.
041     * <p>
042     * Please see <a href="https://issues.apache.org/jira/browse/DBCP-333">the DBCP bug</a> and
043     * <a href="https://github.com/apache/commons-dbcp/blob/DBCP_1_4_x_BRANCH/src/java/org/apache/commons/dbcp/BasicDataSource.java#L1588-L1660">
044     * the fixed method </a>
045     * for details.
046     * <p>
047     * Please also see how OpenJPA
048     * <a href="http://openjpa.apache.org/builds/2.2.1/apache-openjpa/docs/ref_guide_integration_dbcp.html"> is integrated</a>
049     * with DBCP.
050     */
051    protected ConnectionFactory createConnectionFactory() throws SQLException {
052        // Load the JDBC driver class
053        Class driverFromCCL = null;
054        if (driverClassName != null) {
055            try {
056                try {
057                    if (driverClassLoader == null) {
058                        driverFromCCL = Class.forName(driverClassName);
059                    } else {
060                        driverFromCCL = Class.forName(driverClassName, true, driverClassLoader);
061                    }
062                } catch (ClassNotFoundException cnfe) {
063                    driverFromCCL = Thread.currentThread(
064                    ).getContextClassLoader().loadClass(
065                            driverClassName);
066                }
067            } catch (Throwable t) {
068                String message = "Cannot load JDBC driver class '" +
069                        driverClassName + "'";
070                logWriter.println(message);
071                t.printStackTrace(logWriter);
072                throw new SQLNestedException(message, t);
073            }
074        }
075
076        // Create a JDBC driver instance
077        Driver driver = null;
078        try {
079            if (driverFromCCL == null) {
080                driver = DriverManager.getDriver(url);
081            } else {
082                // Usage of DriverManager is not possible, as it does not
083                // respect the ContextClassLoader
084                driver = (Driver) driverFromCCL.newInstance();
085                if (!driver.acceptsURL(url)) {
086                    throw new SQLException("No suitable driver", "08001");
087                }
088            }
089        } catch (Throwable t) {
090            String message = "Cannot create JDBC driver of class '" +
091                    (driverClassName != null ? driverClassName : "") +
092                    "' for connect URL '" + url + "'";
093            logWriter.println(message);
094            t.printStackTrace(logWriter);
095            throw new SQLNestedException(message, t);
096        }
097
098        // Can't test without a validationQuery
099        if (validationQuery == null) {
100            setTestOnBorrow(false);
101            setTestOnReturn(false);
102            setTestWhileIdle(false);
103        }
104
105        // Set up the driver connection factory we will use
106        String user = username;
107        if (user != null) {
108            connectionProperties.put("user", user);
109        } else {
110            log("DBCP DataSource configured without a 'username'");
111        }
112
113        String pwd = password;
114        if (pwd != null) {
115            connectionProperties.put("password", pwd);
116        } else {
117            log("DBCP DataSource configured without a 'password'");
118        }
119
120        ConnectionFactory driverConnectionFactory = new DriverConnectionFactory(driver, url, connectionProperties);
121        return driverConnectionFactory;
122    }
123}