1 /*
2  * Copyright (C) 2013, 2014 Brett Wooldridge
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package com.zaxxer.hikari.util;
17
18 import java.io.PrintWriter;
19 import java.sql.Connection;
20 import java.sql.Driver;
21 import java.sql.DriverManager;
22 import java.sql.SQLException;
23 import java.sql.SQLFeatureNotSupportedException;
24 import java.util.Enumeration;
25 import java.util.Map.Entry;
26 import java.util.Properties;
27
28 import javax.sql.DataSource;
29
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 public final class DriverDataSource implements DataSource
34 {
35    private static final Logger LOGGER = LoggerFactory.getLogger(DriverDataSource.class);
36    private static final String PASSWORD = "password";
37    private static final String USER = "user";
38
39    private final String jdbcUrl;
40    private final Properties driverProperties;
41    private Driver driver;
42
43    public DriverDataSource(String jdbcUrl, String driverClassName, Properties properties, String username, String password)
44    {
45       this.jdbcUrl = jdbcUrl;
46       this.driverProperties = new Properties();
47
48       for (Entry<Object, Object> entry : properties.entrySet()) {
49          driverProperties.setProperty(entry.getKey().toString(), entry.getValue().toString());
50       }
51
52       if (username != null) {
53          driverProperties.put(USER, driverProperties.getProperty("user", username));
54       }
55       if (password != null) {
56          driverProperties.put(PASSWORD, driverProperties.getProperty("password", password));
57       }
58
59       if (driverClassName != null) {
60          Enumeration<Driver> drivers = DriverManager.getDrivers();
61          while (drivers.hasMoreElements()) {
62             Driver d = drivers.nextElement();
63             if (d.getClass().getName().equals(driverClassName)) {
64                driver = d;
65                break;
66             }
67          }
68
69          if (driver == null) {
70             LOGGER.warn("Registered driver with driverClassName={} was not found, trying direct instantiation.", driverClassName);
71             Class<?> driverClass = null;
72             ClassLoader threadContextClassLoader = Thread.currentThread().getContextClassLoader();
73             try {
74                if (threadContextClassLoader != null) {
75                   try {
76                      driverClass = threadContextClassLoader.loadClass(driverClassName);
77                      LOGGER.debug("Driver class {} found in Thread context class loader {}", driverClassName, threadContextClassLoader);
78                   }
79                   catch (ClassNotFoundException e) {
80                      LOGGER.debug("Driver class {} not found in Thread context class loader {}, trying classloader {}",
81                                   driverClassName, threadContextClassLoader, this.getClass().getClassLoader());
82                   }
83                }
84
85                if (driverClass == null) {
86                   driverClass = this.getClass().getClassLoader().loadClass(driverClassName);
87                   LOGGER.debug("Driver class {} found in the HikariConfig class classloader {}", driverClassName, this.getClass().getClassLoader());
88                }
89             } catch (ClassNotFoundException e) {
90                LOGGER.debug("Failed to load driver class {} from HikariConfig class classloader {}", driverClassName, this.getClass().getClassLoader());
91             }
92
93             if (driverClass != null) {
94                try {
95                   driver = (Driver) driverClass.newInstance();
96                } catch (Exception e) {
97                   LOGGER.warn("Failed to create instance of driver class {}, trying jdbcUrl resolution", driverClassName, e);
98                }
99             }
100          }
101       }
102
103       final String sanitizedUrl = jdbcUrl.replaceAll("([?&;]password=)[^&#;]*(.*)""$1<masked>$2");
104       try {
105          if (driver == null) {
106             driver = DriverManager.getDriver(jdbcUrl);
107             LOGGER.debug("Loaded driver with class name {} for jdbcUrl={}", driver.getClass().getName(), sanitizedUrl);
108          }
109          else if (!driver.acceptsURL(jdbcUrl)) {
110             throw new RuntimeException("Driver " + driverClassName + " claims to not accept jdbcUrl, " + sanitizedUrl);
111          }
112       }
113       catch (SQLException e) {
114          throw new RuntimeException("Failed to get driver instance for jdbcUrl=" + sanitizedUrl, e);
115       }
116    }
117
118    @Override
119    public Connection getConnection() throws SQLException
120    {
121       return driver.connect(jdbcUrl, driverProperties);
122    }
123
124    @Override
125    public Connection getConnection(final String username, final String password) throws SQLException
126    {
127       final Properties cloned = (Properties) driverProperties.clone();
128       if (username != null) {
129          cloned.put("user", username);
130          if (cloned.containsKey("username")) {
131             cloned.put("username", username);
132          }
133       }
134       if (password != null) {
135          cloned.put("password", password);
136       }
137
138       return driver.connect(jdbcUrl, cloned);
139    }
140
141    @Override
142    public PrintWriter getLogWriter() throws SQLException
143    {
144       throw new SQLFeatureNotSupportedException();
145    }
146
147    @Override
148    public void setLogWriter(PrintWriter logWriter) throws SQLException
149    {
150       throw new SQLFeatureNotSupportedException();
151    }
152
153    @Override
154    public void setLoginTimeout(int seconds) throws SQLException
155    {
156       DriverManager.setLoginTimeout(seconds);
157    }
158
159    @Override
160    public int getLoginTimeout() throws SQLException
161    {
162       return DriverManager.getLoginTimeout();
163    }
164
165    @Override
166    public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException
167    {
168       return driver.getParentLogger();
169    }
170
171    @Override
172    public <T> T unwrap(Class<T> iface) throws SQLException
173    {
174       throw new SQLFeatureNotSupportedException();
175    }
176
177    @Override
178    public boolean isWrapperFor(Class<?> iface) throws SQLException
179    {
180       return false;
181    }
182 }
183